类型系统

索引访问

Indexed Access Typesopen in new window

我们可以使用一个索引访问类型去查询另一个类型指定属性的类型。

索引不存在的属性

type Person = { age: number; name: string; alive: boolean };
// Property 'alve' does not exist on type 'Person'.
type I1 = Person["alve"];
1
2
3

若是索引不存在的属性,将得到报错。

获取数组元素的类型

const MyArray = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
  { name: "Eve", age: 38 },
];

// type Person = {
//     name: string;
//     age: number;
// }
type Person = typeof MyArray[number];

// type Age = number
type Age = typeof MyArray[number]["age"];
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意,typeof MyArray[number]实际上是(typeof MyArray)[number]typeof MyArray获取MyArray的类型,得到数组类型{name: string; age: number;}[],最后获取数组项的类型。

T[K]

  • T[K],获取 T 中key为 K 的类型组成的联合类型,其中 K 是字面量类型或其联合类型。
  • T[keyof T],可以获取到 T 中所有key的类型组成的联合类型。
  • T[keyof K],获取到的是 T 中同时存在于 T 和 K 的key的类型组成的联合类型。

注意:如果[]中的key有不存在 T 中的,则该key的类型是any;因为 TypeScript 也不知道该key最终是什么类型,所以是any,且也会报错。

类型推导

类型断言

两种使用方式:

  • value as Type
  • <Type>value

类型守卫

类型守卫(Type Guard)。

可用于类型守卫的运算符:

  • switch/case,当联合类型里的各个类型都存在一个相同名称的字面量类型的属性,且字面量类型各不相同
  • if/else + in,当联合类型里的某个类型存在一个独有的属性
  • if/else + typeof
  • instanceof,检查是否是联合类型里某个类类型的实例
  • ==!====!==,用这些操作符与字面量的值比较时
  • 自定义守卫open in new window

类型收窄

类型收窄,Type Narrowing

结构化类型

TypeScript 的类型检查,是通过检查值的形状来判断的。

class Person {
    constructor(
        public firstName: string,
        public lastName: string
    ) {}
}

class Student {
    constructor(
        public firstName: string,
        public lastName: string,
        public marks: number
    ) {}
}

// print fullname of a `Person` object
function getFullName( p: Person ): string {
    return `${ p.firstName } ${ p.lastName }`;
}

var ross = new Person( 'Ross', 'Geller' );
var monica = new Student( 'Monica', 'Geller', 84 );

console.log( 'Ross =>', getFullName( ross ) );
console.log( 'Monica =>', getFullName( monica ) );
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

上述示例中,尽管我们没有显式地提及Student类继承了Person类,但是 TypeScript 允许Student类的实例作为p参数的值。这是因为Student类的实例monica具有string类型的firstNamelastName属性,TypeScript 在校验参数p时只是校验传入的值是否具有与Person类相同的结构,而不是校验是否是Person的实例。

这也证明 TypeScript 是结构化类型语言,也称为鸭式类型,即“如果它走起来像鸭子,叫起来像鸭子,游起来像鸭子,那么它就是个鸭子”。由于Student类型拥有Person类型的行为,因此 TypeScript 认为它也是个Person

你可以将这些法则应用于类。由于 TypeScript 里的类类型隐式地定义了一个包括了类公共成员的接口,因此你可以将相同的法则应用到接口上。

interface Person {
    firstName: string;
    lastName: string;
}

interface Student {
    firstName: string;
    lastName: string;
    marks: number;
}

// print fullname of a `Person` object
function getFullName( p: Person ): string {
    return `${ p.firstName } ${ p.lastName }`;
}

var ross: Person = {
    firstName: 'Ross',
    lastName: 'Geller'
};

var monica: Student = {
    firstName: 'Monica',
    lastName: 'Geller',
    marks: 84,
};

console.log( 'Ross =>', getFullName( ross ) );
console.log( 'Monica =>', getFullName( monica ) );
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

当类型 A 具有类型 B 的所有属性,则 A 成为 B 的子类型。

上述示例里,Student接口即为Person接口的子类型。类型为Student的对象monica,可以作为类型为Person的参数p的值,这种行为称为结构化子类型

但结构化子类型并不是在任何场景下都是合法的,比如:

interface Person {
    firstName: string;
    lastName: string;
}

// accept an argument of type `Person
let printPerson = ( person: Person ): void => {
    console.log( `Hello, ${ person.firstName } ${ person.lastName }.` );
};

// legal
let ross = { firstName: 'Ross', lastName: 'Geller', gender: 'Male' };
printPerson( ross );

// illegal
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
printPerson( { firstName: 'Ross', lastName: 'Geller', gender: 'Male' } );

// legal
let monica: Person;
let monana = { firstName: 'Monica', lastName: 'Geller', gender: 'Male' };
monica = monana;

// illegal
// Error: Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
let p: Person = { firstName: 'Ross', lastName: 'Geller', gender: 'Male' };
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

TypeScript 允许使用一个变量引用代替子类型(TypeScript 能从字面量值推导出变量的类型,因而隐式地产生子类型),但是不允许直接使用字面量值(否则容易误导)。

参考文档