TS入门013ts高级类型上

类型的且运算

  • 你的新类型 c 必须同时满足 A 和 B接口的约束,否则报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface A {
name:string;
age:number
}

interface B {
name:string;
grade:number
}

const c: A & B = {
name:'aaa',
age:11,
grade:100
}

React 里使用这个 且运算

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';

const Layout : React.FunctionComponent & { Header :React.FunctionComponent } = ()=>{
return (
React.createElement('div',null,'hi')
)
}

Layout.Header = ()=>{
return (
React.createElement('div',null,'hi')
)
}

垂直写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

interface Layout2 extends React.FunctionComponent{
Header:React.FunctionComponent
}

const Layout2:Layout2 = ()=>{
return (
React.createElement('div',null,'hi')
)
}
Layout2.Header = ()=>{
return (
React.createElement('div',null,'hi')
)
}

小知识点:接口名和变量能一样吗?

  • 可以 ts 自己就能识别

类型的或运算

  • 都满足
  • 满足其中一个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
interface A {
name:string;
age:number
}

interface B {
name:string;
grade:number
}

// 都满足
const c: A | B = {
name:'aaa',
age:11,
grade:100
}

// 满足单个
const c1: A | B = {
name:'aaa',
age:11
}
// 满足单个
const c2: A | B = {
name:'aaa',
grade:11
}
}

或运算的歧义

1
2
3
4
5
6
7
8
9
function add(a : number|string,b : number|string){
// 标红
// 运算符“+”不能应用于类型“string | number”和“string | number”。
return a + b;
}

add(1,2)
add('1','2')
add(1,'2')

你想到的解决办法 泛型 其实也不行,因为你怎么保证 这俩类型可以”+”呢?

1
2
3
4
function add<T>(a : T,b : T ){
//运算符“+”不能应用于类型“T”和“T”
return a + b;
}

答案是使用 重载

1
2
3
4
5
6
7
function add(a:string,b : string ):string;
function add(a:number,b : number ):number;
function add(a:any,b : any ):any{
return a + b;
}
add(1,2)
add('1','2')

类型别名 type

  • 给一个已知的类型取一个别名

别名 type 和 interface的区别

  • interface 是声明了一个新的类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import React from 'react';

const Layout : React.FunctionComponent & { Header :React.FunctionComponent } = ()=>{
return (
React.createElement('div',null,'hi')
)
}

Layout.Header = ()=>{
return (
React.createElement('div',null,'hi')
)
}

// 上面的 & 运算之后 类型会显得很长
// 别名的使用:给已知类型一个名字
type MyLayout = React.FunctionComponent & { Header :React.FunctionComponent }
const Layout2 : MyLayout = ()=>{
return (
React.createElement('div',null,'hi')
)
}

Layout2.Header = ()=>{
return (
React.createElement('div',null,'hi')
)
}

字面量类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
interface People {
gender : '男' | '女'
}

const a:People = {
// 报错 ,限定了 gender 必须是 '男' or '女'
// gender :'人妖'
gender:'男'
}

// 方向
type Dir = '上' | '下' | '左' | '右'
// const dir: Dir = 'e'; // 直接报错
const dir : Dir = '上'

// 字面量类型 还能包含多个类型取值
type Aa= 1 | 2 | 3 | 4 | 5 | 6
type Bb = true | false | 6 | '上'

// 丰富的类型搭配
interface People {
gender : '男' | '女',
name? : string;
name2: string | undefined;
name3: string | undefined | null;
}

this 也有类型

  • 链式操作
  • this多态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
class Calc {
public value: number
constructor(n:number){
this.value = n
}
add(n:number){
this.value += n;
return this;
}
multiple(n:number){
this.value *= n;
return this;
}
}

// 多态
class BiggerCalc extends Calc {
sin(){
this.value = Math.sin(this.value);
return this;
}
}

const c = new Calc(1);
c.add(1).multiple(2);

const bc = new BiggerCalc(1);
bc.add(1).multiple(2).sin(3).add(1);
}

this的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
function fn(n:number){
console.log(n)
}
fn(1);
fn.call('string',1);

// this 也可以是参数,我怎么指定this的类型
function fn2(this:string,n:number){
console.log(n)
}
// 报错了
// fn2(1); this is undefined
fn2.call('string',1);

// 如何解决这个 不指定this的报错呢? void
function fn3(this: string | void,n:number){
console.log(n)
}
fn3(1);
fn3.call('string',1);
fn3.call(true,1) // 居然也成功了 !!!
// 因为 ts 对 call 不做类型检查

}
  • 这个一般只有写底层库的时候可能用到

索引类型 keyof 和 T[K]

索引类型

假如你有个函数,它的参数是对象 opt = {name:xxx,bbb:xxx,ccc:xxx ...} 可无限扩展的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
{
const fn = (options)=>{}

var options = {
time: new Date,
isEdit: true,
size : 20,
name : 'xxx'
}

fn(options)

// 如何声明 options 的类型呢?
interface fnOptions {
// 我的 key 必须是 string ,value 是任意值
[K:string]:any
}
// 以前你要这样
interface fnOptions2{
size:number
name:string
}
const fn2 = (options:fnOptions)=>{}


// T[K] 是啥
// 只能传递 前面有的 key
function pluck<T, K extends keyof T>(objext:T,keys:Array<K>) {
// T - {name:string,age:number,grade:number}
// keyof T - 'name'|'age'|'grade'
// K extends keyof T - 'name'|'age'|'grade'
}


// 第二个参数数组 必须是 第一个参数里的 key 否则报错
pluck({name:'hjx',age:18,grade:100},['name','age'])

interface Person{
name:string;
age:number;
grade:number;
}
type X = keyof Person; // X 只能是 Person里的属性

// T[K]返回值啥意思
function pluck2<T, K extends keyof T>(objext:T,keys:Array<K>): T[K][] {
// 报错,因为 T[K] 里面是对应每个 key的类型 是动态的
// return 'string';
return keys.map(key => objext[key])
}
pluck2({name:'hjx',age:18,grade:100},['name','age'])
/*
['hjx',18]
=>
Array<Person[name]|Person[age]>
=>
Array<T[K]>
=>
T[K][]
*/

}

Readonly / Partial

Readonly

  • Readonly<T>
  • readonly field
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
interface Person {
name:string;
age:number;
}

const p :Person = {
name:'aa',
age:12
}
p.name = 'bb';



// Readonly<T> 全部只读
const p2 :Readonly<Person> = {
name:'hjx',
age:18
}
// p2.name = 1; // 报错

interface ReadonlyPerson {
readonly name:string,
age:number;
}

const p3 :ReadonlyPerson = {
name:'hjx',
age:18
}
// p3.name = 1; // 报错
p3.age = 22

}

Partial

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
interface Person {
name:string;
age:number;
grade:number;
}

interface Person2 {
name?:string;
age?:number;
grade?:number;
}

type Person3 = Partial<Person>;
// 等价于 Person2


type P4 = Required<Person3>

}

参考代码 ts013