表达式!
typeof 值
keyof 数据类型
(输入索引类型查询)类型[键名]
(索引访问类型)in
readonly
this
类型1 extends 类型2 ? 类型3 : 类型4
(条件类型)infer
...
展开类型///
(三斜线指令)namespace
(命名空间)名字.d.ts
(声明文件)// @ts-ignore
参考:TypeScript 入门教程、TypeScript 使用手册(中文版)翻译、TypeScript Deep Dive 中文版。
TypeScript是JS的一个超集,主要提供了类型系统和对ES6的支持。
数据类型
Number
、String
、Boolean
、Symbol
、BigInt
、Object
:(大写的)几乎在任何时候都不应该被用作一个类型。
Object
类型:匹配任何值(基本数据类型+引用数据类型)。除了
Object
之外的引用类型(如:Function
、Date
),他们的实例类型用(大写的)构造函数名称(如:: Function
、: Date
)。
基本数据类型
number
string
boolean
symbol
bigint
undefined
undefined
可以赋值给可选参数(?:
)。
a?: 某类型
表示:a
不存在或不传参,也可以是某类型
或undefined
。a: 某类型 | undefined
表示:a
必须存在或必传参,类型是某类型
或undefined
。
null
undefined
和null
值 可以赋值给所有类型的变量(除了never
类型);undefined
和null
类型 是所有类型的子类型(除了never
类型)。- 若配置
strictNullChecks
,则:undefined
只能赋值给类型any
、undefined
、void
;null
只能赋值给类型any
、null
。
void
null
(只在配置strictNullChecks
未指定时)或undefined
赋值。函数返回值类型void,表示:return null
(只在配置strictNullChecks
未指定时)、return undefined
、return
、没有return
。
返回值类型
是协变的)Promise的默认返回可以用
e.g. ```typescript function a (): Promisevoid
(不能用)undefined
{ return new Promise((resolve, reject) => { resolve(); }).then(() => { // 支持:`return null`(只在配置`strictNullChecks`未指定时)、`return undefined`、`return`、没有`return`。 }); } function b (): Promise { return new Promise((resolve, reject) => { resolve(); }).then(() => { return undefined // 或:return null(只在配置`strictNullChecks`未指定时) }); } ``` </details>
never
表示永不存在的值的类型
e.g. 总是会抛出异常 或 根本就不会有返回值 的函数表达式;变量也可能是never类型,当它们被永不为真的类型保护所约束时。
当类型不存在时通常返回never
e.g.
number & string
、Extract<string | number , boolean>
)。
never
可以赋值给所有类型的变量(包括undefined
和null
),never
是所有类型的子类型。never
类型的变量仅能被never
类型的值赋值。
e.g.
```typescript // 返回never的函数必须存在无法达到的终点 function error (message: string): never { throw new Error(message) } // 推断的返回值类型为never function fail () { return error('Something failed') } // 返回never的函数必须存在无法达到的终点 function infiniteLoop (): never { while (true) { } } // 变量 let x: never let y: number let z: undefined x = 123 // 报错,number 类型不能转为 never 类型 x = undefined // 报错,undefined 类型不能转为 never 类型 x = (() => { throw new Error('exception')})() // never 类型可以赋值给 never 类型 y = (() => { throw new Error('exception')})() // never 类型可以赋值给 number 类型 z = (() => { throw new Error('exception')})() // never 类型可以赋值给 undefined 类型 ```
any
能够兼容所有的类型(包括它自己)。当使用any
时,基本上是在告诉TS编译器不要进行任何的类型检查,TS将会把类型检查关闭。
未声明类型的(且没有类型推论的)被认为是
any
。
unknown
unknown
。unknown
不可以赋值给其它类型,除了它自己和any
之外。unknown
没有被类型断言或js代码细化到一个确切类型之前,不允许在其上进行任何操作。try-catch
抓到的是unknown
,需要类型保护(e.g. if (err instanceof Error) {){}
)之后才能认为err
是Error
类型。?.
无效,只能用类型保护才能使用其属性。object
或{}
表示非原始类型/非基本数据类型(除了boolean
、number
、string
、symbol
、bigint
、undefined
、null
之外的类型)。允许给它赋任意值和访问Object.prototype
上的属性,但不能调用任意其他方法,即便它真的有这些方法。
e.g.
```typescript let obj1: object obj1 = [] obj1.toString() // 允许访问Object.prototype上的属性 obj1.a() // 报错,只允许使用Object.prototype上的属性 obj1.length // 报错,只允许使用Object.prototype上的属性 let obj2: { a } obj2 = { a: () => {} } obj2.toString() // 允许访问Object.prototype上的属性 obj2.a() // 允许访问定义的属性a obj2.length // 报错,只允许使用Object.prototype上的属性 ```
对象类型
用接口
定义。
属性名: 数据类型
),对象不允许多于或少于约定的属性数量(若有索引签名
时,则允许多定义属性)。属性名?: 数据类型
)。只读类型(readonly 属性名: 数据类型
),创建对象时必须给此属性赋值,并且之后不能修改此属性。
作为变量使用用
const
,作为属性使用用readonly
。
索引签名
([任意名: string]: 数据类型
),确定属性、可选属性、只读属性的类型都必须是索引签名
的类型的子集。
尽量不要把 索引签名
与 属性 混合使用。若属性名称中有拼写错误,则这个错误不会被捕获到
当 属性 和 索引签名
直接合并会出错的情况(当 属性的类型 并非 索引签名
的类型 的子集时),也可以用&
,只是不能用这个交叉类型创建对象
e.g.
```typescript interface Person { name: string func(): string score?: number // -> 不存在 或 number | undefined readonly id: number // 索引签名:描述对象的属性 [任意名: string]: string | Function | number | undefined | boolean // 必须包含:所有其他属性的类型的联合类型(|) } let tom: Person = { name: 'Tom', func(){ return '' }, id: 1, xx: 22 } tom.id = 2 // 报错,readonly tom.x = 'x' tom.xx = 'xx' tom.xxx = 1n // 报错,1n不是 string | Function | number | undefined | boolean ```
: { 属性: 数据类型, }
(内联类型注解)
{ [任意名: string]: any }
等价于Record<string, any>
,表示对象类型,比、object
更严格定义对象类型。{}
: 类名
取实例的类型,而不是类的类型,不包含类的所有 和 静态属性/方法
。构造函数
e.g.
```typescript class A { a: number = 2; aa?: number = 2; b: () => void = () => {}; bb?: () => void = () => {}; c() {} cc?() {} static d: any; } let a1: A = new A(); let a2: A = { a: 1, b() {}, c() {} }; let a3: A = {}; // 报错,需要a、b、c属性 ```
Freshness(更严格的对象字面量
检查)
只会发生在对象字面量
上的错误提示。
e.g.
```typescript function logName(something: { name: string }) { console.log(something.name); } const person = { name: 'matt' }; const animal = { name: 'cow', diet: 'vegan, but has milk of own specie' }; const randow = { note: `I don't have a name property` }; logName(person); // ok logName(animal); // ok logName(randow); // Error: 没有 `name` 属性 logName({ name: 'matt' }); // ok logName({ name: 'matt', job: 'being awesome' }); // Error: 对象字面量只能指定已知属性,`job` 属性在这里并不存在。 logName({}); // Error: 对象字面量只能指定已知属性,非可选 ```
数组类型
数据类型[]
e.g.
```typescript let arr1: (number | string)[] = [1, "1"]; let arr2: { name: string; age: number }[] = [ { name: "", age: 0 }, { name: "1", age: 1 }, ]; class A { name: string = ""; age: number = 0; sex?: boolean = true; } let arr3: A[] = [ { name: "", age: 0, sex: false }, { name: "1", age: 1 }, ]; ```
泛型Array<数据类型>
e.g.
let arr: Array<number | string> = [1, '1']
ReadonlyArray<数据类型>
与Array<数据类型>
相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改(readonly)。
e.g.
```typescript let a: number[] = [1, 2, 3, 4] let ro: ReadonlyArray= a // 允许,普通数据类型 赋值给 ReadonlyArray ro[0] = 12 // 报错 ro.push(5) // 报错 ro.length = 100 // 报错 let b: number[] = ro // 报错,ReadonlyArray赋值给一个普通数组也是不可以的 let c: number[] = ro as number[] // 允许,类型断言重写 ``` </details>
用接口
定义
用索引签名
来定义索引和项。
interface NumberArray {
[任意名: number]: number | string
}
let arr: NumberArray = [1, '1']
元组(Tuple)
规定数组
每一项的数据类型:
push
等可以大于规定长度,但要用前面所有数据类型的联合类型。元组[数字]
可以获取内部某一项的类型
e.g.
```typescript let arr1: [string, number] = ['string', 1] arr1.push(2) arr1.push(true) // 报错,只能添加联合类型 let arr2: [string, number] = ['string', 1, '啊'] // 报错,直接赋值不能多于约定长度 let arr3: [string, number] = ['string'] // 报错,直接赋值不能少于约定长度 type A = [number, string] type B = A[0] // -> number ```
将索引签名
设置为只读
e.g.
```typescript interface ReadonlyStringArray { readonly [任意名: number]: string; } let myArray: ReadonlyStringArray = ["Alice", "Bob"]; myArray[2] = "Mallory"; // error。readonly ```
枚举被编译为.js是数组。
函数类型
支持:函数声明、函数表达式。
函数表达式定义类型方式:Function
或 参数类型 => 返回类型
或 接口
(、类型别名、内联类型注解)
若函数表达式需要表示重载,则只能通过 接口
(、类型别名、内联类型注解) 定义。
函数声明使用定义类型方式:内联类型注解
支持:可选参数、默认参数、剩余参数。
可选参数 和 默认参数 不能同时设置(默认参数有可选参数的含义)。
e.g. 不允许:
;x?: number = 1
x: number = 1
,可以传number
或undefined
,或不传参。
设置 所有参数类型(空参数也满足):(...args: 数组类型)
。
?
、或默认参数=
、或剩余参数...
,则允许少传入参数)。new
可实例化
e.g.
```typescript interface A1 { // 或:类型别名、内联类型注解 new (): string; } // 使用 declare const A1Func: A1; const bar1 = new A1Func(); // bar1 被推断为 string 类型 type A2 = new () => string // 使用 declare const A2Func: A2 const bar2 = new A2Func(); // bar2 被推断为 string 类型 ```
e.g.
```typescript // 函数声明 // 可选参数 function sum1 (x: number, y?: number): string { if (y) { return x + '' } else { return x + y + '' } } // 默认参数 function sum2 (x: number = 1, y: number = 2): string { // 参数要使用默认参数:不传 或 传`undefined` return x + y + '' } // 剩余参数 function sum3 (x: number, ...items: number[]): string { return x + items.reduce((a, b) => a + b, 0) + '' } // 函数表达式 // 类型推论 let mySum1 = function (x: number, y: number): string { return x + y + '' } // 显式定义(不是类型推论) let mySum2: (xx: number, yy: number) => string = function (x: number, y: number): string { // 定义的参数名和实现的函数参数名不用一致 return x + y + '' } // 接口(或 类型别名) interface mySum { // 描述方法(没有属性名) (xx: number, yy: number): string // 定义的参数名和实现的函数参数名不用一致 } let mySum3: mySum // 显式定义(不是类型推论) mySum3 = function (x, y) { // 类型推论 // 或:mySum3 = function (x: number, y: number): string { // 显式定义(不是类型推论) return x + y + '' } // 内联类型注解 function identity(arg: T): T { return arg; } let myIdentity: { (arg: T): T} = identity; ``` </details>
仅定义数据类型、不实现的方法都只有
()
、没有{}
((参数: 类型): 类型
):函数声明的重载、interface/type/内联类型注解 中的方法
、declare class 中的 方法
、abstract class 中的 abstract 方法
。
支持:重载
e.g.
```typescript // 函数声明 function reverse1(x: number): number; function reverse1(x: string): string; function reverse1(x: number | string): number | string { if (typeof x === "number") { return Number(x.toString().split("").reverse().join("")); } else { return x.split("").reverse().join(""); } } // 函数表达式 interface FuncType { (a: number): number; (a: string): string; }; const a: FuncType = <T extends string | number>(a: T): T => a ```
TS中的函数重载没有任何运行时开销。
内置对象类型
浏览器环境
TypeScript核心库的定义文件定义了所有浏览器环境需要用到的类型(预置在TypeScript中)。
Node.js
Node.js不是内置对象的一部分,需引入第三方声明文件:npm install @types/node --save-dev
。
返回Promise类型
Promise<resolve的类型>
仅能定义完成的Promise实例,不能定义失败的。失败的Promise实例总是认为是unknown
(与try-catch
中catch
的参数类型一致)。
e.g.
```typescript function a(): Promise<number | string> { // 仅能定义resolve return new Promise((resolve, reject) => { const random = Math.random(); if (random < 0.3) { resolve(1); // number } else if (random < 0.6) { resolve("1"); // string } else { reject(false); // 完全忽略reject } }); } function b(): Promise{ return Promise.resolve(123); // 报错,要求 string } function c(): Promise { return Promise.reject(123); // 没问题,完全忽略reject } ``` </details>
类
访问修饰符(Access Modifiers)
规定类的属性/方法的访问权限。
public
(默认):公有的,在任何地方都可以被访问。private
:私有的,只能在声明的类中访问。(继承和实例化对象不能访问)
若类的构造函数设置为
private
,则这个类不能实例化、也不能够被继承。
protected
:受保护的,只能在声明的类中、声明的继承子类中访问。(实例化对象不能访问)
若类的构造函数设置为
e.g. ```typescript class Person { protected constructor () {} } class Employee extends Person { constructor () { super() } } let john = new Person() // 报错,Person的构造函数是protected,只能够被子类调用 let howard = new Employee() ```protected
,则这个类不能实例化,但继承的子级能够调用这个构造函数(super()
)。
e.g.
```typescript class Animal { public constructor (name, age, sex) { // (默认值会在编译后的.js的构造函数最前面加上:)this.age = 100 this.name = name this.age = age // 类的只读属性在构造函数里初始化 this.sex = sex } public name: string // 类的只读属性,必须在声明时默认赋值 或 在构造函数里初始化。 private readonly age: number = 100 // 类的只读属性在声明时默认赋值 protected sex: boolean public getName (): string { return this.name } private getAge (): number { return this.age } protected getSex (): boolean { return this.sex } } let a = new Animal('Jack', 5, true) a.name = 'Tom' console.log(a.age) // 报错,private console.log(a.sex) // 报错,protected console.log(a.getName()) console.log(a.getAge()) // 报错,private console.log(a.getSex()) // 报错,protected class Cat extends Animal { constructor (name, age, sex) { super(name, age, sex) console.log(this.name) console.log(this.age) // 报错,private console.log(this.sex) console.log(super.getName()) console.log(super.getAge()) // 报错,private console.log(super.getSex()) } } let b = new Cat('Jacky', 10, false) b.name = 'Tomy' console.log(b.age) // 报错,private console.log(b.sex) // 报错,protected console.log(b.getName()) console.log(b.getAge()) // 报错,private console.log(b.getSex()) // 报错,protected ```
参数属性
若在类的构造函数的参数上设置访问修饰符(private/public/protected
),则在实例化时会用参数名新建一个实例属性/方法。
e.g.
```typescript // A1和A2编译出的.js结果一致 class A1 { private a: string protected readonly b: number constructor (临时属性1: string, 临时属性2: number) { this.a = 临时属性1 this.b = 临时属性2 } } class A2 { constructor (private a: string, protected readonly b: number) { } } ```
class-extends
重载属性
public
仅可被public
重载private
不可被重载protected
仅可被public
或protected
重载typeof 类名
取类的类型,而不是实例的类型,包含类的所有静态属性/方法
和prototype
。
e.g.
```typescript class Greeter { static staticGreeting = 'Hello, there' greeting: string greet () { return Greeter.staticGreeting } } let greeter1: Greeter // 实例(实例属性/方法、类.prototype.属性/方法) greeter1 = new Greeter() console.log(greeter1.greet(), greeter1.greeting) let greeterMaker: typeof Greeter // 类(静态属性/方法、`prototype`) greeterMaker = Greeter greeterMaker.staticGreeting = 'Hey there' let greeter2: Greeter greeter2 = new greeterMaker() console.log(greeter2.greet(), greeter2.greeting) type A = keyof typeof Greeter // -> 'prototype' | 'staticGreeting' type B = keyof Greeter // -> 'greeting' | 'greet' ```
abstract
抽象类、抽象方法。
抽象方法必须被子类实现(抽象类自己不能定义自己的抽象方法的实现)
抽象方法仅允许出现在抽象类中。
e.g.
```typescript abstract class Animal { // 抽象类 public name: string public constructor (name: string) { this.name = name } public abstract sayHi () // 抽象方法 public func () {} } class Cat extends Animal { public sayHi (): void { console.log(`Meow, My name is ${this.name}`) } } let cat: Cat = new Cat('Tom') cat.sayHi() ```
implements
class
实现interface
(仅对class
的实例属性/方法进行类型检查、不检查class
的静态属性/方法)
e.g.
```typescript interface Alarm { alert(num: number): void; // 接口上的属性 被class实现必须是public } interface Light { lightOn(str: string): boolean; lightOff?(): void; } class Car1 implements Light { public lightOn(str: string) { console.log("Car1 light on", str, this.x1, this.x2); return true; } protected x1() {} private x2() {} } class Car2 extends Car1 implements Alarm, Light { alert(num: number) { console.log("Car2 alert", num); } public lightOn(str: string) { console.log("Car2 light on", str,this.xx1, this.xx2); return true; } lightOff() { console.log("Car2 light off"); } protected xx1() {} private xx2() {} } ```
类型兼容性
接口(Interfaces)
interface
定义对象
、数组
、函数
的形状(Shape:数量和数据类型)
e.g.
- 对象类型:
interface X { a(): any, b: any }
- 函数类型:
interface Y { (): any }
- 数组类型:
interface Z { [任意名: number]: any }
还可以定义混合类型(一个对象同时作为函数和对象使用)。
e.g.
```typescript interface Counter { (start: number): string // 描述方法(没有属性名) interval: number // 描述对象的属性 reset (): void // 描述对象的方法(有属性名) } function getCounter (): Counter { let counter =function (start: number) { } counter.interval = 123 counter.reset = function () { } return counter } let c = getCounter() c(10) // 作为函数使用 c.reset() // 作为对象使用 c.interval = 5.0 // 作为对象使用 ``` </details>
对类
的一部分行为进行抽象(类
实现接口
)
extends
与
class-extends
的不完全一致。
接口继承。interface B extends A
满足:B可以赋值给A(概括下面的继承
+重载
)。
接口继承接口
e.g.
```typescript interface Alarm { alert () } interface LightableAlarm extends Alarm { lightOn () lightOff () } const obj: LightableAlarm = { alert(){}, lightOn(){}, lightOff(){} } ```
接口继承类
e.g.
```typescript class Point { x?: number y: number } interface Point3d extends Point { z: number } let point3d: Point3d = { y: 2, z: 3 } ```
接口会继承类的访问修饰符(public
、private
、protected
)
当一个接口继承了一个拥有private
或protected
的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。
e.g.
```typescript class Control { private state: any; } interface SelectableControl extends Control { select(): void; } class Button extends Control implements SelectableControl { select() {} } class TextBox extends Control { select() {} } class ImageControl implements SelectableControl { // Error: Class 'ImageControl' incorrectly implements interface 'SelectableControl'. // Types have separate declarations of a private property 'state'. private state: any; select() {} } ```
接口继承别名
e.g.
```typescript type Name = { name: string; } interface User extends Name { age: number; } ```
支持:合并
同名的interface会自动合并(type
不可以有同名的定义)。
接口中的属性在合并时会简单的合并到一个接口中
合并的相同属性的类型必须是相同的。
接口中属性的 内联类型注解 可以重载覆盖,但是需要用更多属性的对象(协变),不能用更少属性的对象
e.g.
```typescript interface a { aa: { a: string; b: string } } interface b extends a { aa: { a: string; b: string; c: string }; } interface c extends a { // 报错,不能用更少的属性覆盖 aa: { a: string; c: string }; } ```
类型别名(Type Alias)
类型别名和
接口
有时很像,区别:
- 类型别名可以作用于:原始值、联合类型、交叉类型、元组以及其它任何需要手写的类型,只需要给它一个语义化的名字即可;
接口
仅定义:对象、数组、函数,但有层次结构,能使用implements
、extends
。
type
:与其原始的类型完全一致;它们只是简单的替代名。
e.g.
```typescript type Name = string type NameResolver = () => string type NameOrResolver = Name | NameResolver function getName(n: NameOrResolver): Name { if (typeof n === 'string') { return n } else { return n() } } ```
可以使用类型别名来在属性里引用自己( 不可以)接口
e.g.
```typescript type Tree= { value: T; left: Tree ; right: Tree ; } type LinkedList = T & { next: LinkedList }; ``` </details>
扩展用&
(接口
用extends
)
e.g.
```typescript type Name1 = { name: string; } type User1 = Name1 & { age: number; }; interface Name2 { name: string; } type User2 = Name2 & { age: number; }; ```
字面量类型
一个字面量是一个集体类型(字符串、数字、布尔值、大数)中更为具体的一种子类型。通过使用字面量类型,可以规定一个字符串、数字、布尔值或大数必须含有的确定值。
e.g.
```typescript type aa = "ease-in" | "ease-out" | "ease-in-out"; type bb = 8 | 16 | 32; type cc = { c1: true; c2: string } | { c1: false; c2: number }; interface MapType { a: "ease-in" | "ease-out" | "ease-in-out"; aa: aa; b: 8 | 16 | 32; bb: bb; c: { c1: true; c2: string } | { c1: false; c2: number }; cc: cc; } const obj: MapType = { a: "ease-in", aa: "ease-in", b: 8, bb: 8, c: { c1: true, c2: "123" }, cc: { c1: false, c2: 13 }, }; ```
模版字面量类型
若在替换字符串的位置是联合类型,则结果类型是由每个联合类型成员构成的字符串字面量的集合。
e.g.
```typescript type World = 'world'; type Greeting = `hello ${World}`; // -> 'hello world' type EmailLocaleIDs = 'welcome_email' | 'email_heading'; type FooterLocaleIDs = 'footer_title' | 'footer_sendoff'; type Id = 'id1' | 'id2' type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_${Id}`; // -> "welcome_email_id1" | "email_heading_id1" | "footer_title_id1" | "footer_sendoff_id1" | "welcome_email_id2" | "email_heading_id2" | "footer_title_id2" | "footer_sendoff_id2" ```
枚举(Enum)
用于取值被限定在一定范围内的场景。语义化、限制值的范围(只允许使用已定义的枚举名)。
使用枚举类型可以为一组数值赋予友好的名字。
普通枚举 及 枚举成员的类型
(除了常数枚举、外部枚举之外,)枚举名映射到枚举值,枚举值也映射到枚举名。
常数项(constant member)
枚举成员会被赋值为从0
开始递增的数字。
e.g.
```typescript // .ts enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat} // 被编译为.js var Days; (function (Days) { Days[Days["Sun"] = 0] = "Sun"; Days[Days["Mon"] = 1] = "Mon"; Days[Days["Tue"] = 2] = "Tue"; Days[Days["Wed"] = 3] = "Wed"; Days[Days["Thu"] = 4] = "Thu"; Days[Days["Fri"] = 5] = "Fri"; Days[Days["Sat"] = 6] = "Sat"; })(Days || (Days = {})); /* 使用测试 */ console.log(Days["Sun"] === 0); // => true console.log(Days["Mon"] === 1); // => true console.log(Days["Tue"] === 2); // => true console.log(Days["Sat"] === 6); // => true console.log(Days[0] === "Sun"); // => true console.log(Days[1] === "Mon"); // => true console.log(Days[2] === "Tue"); // => true console.log(Days[6] === "Sat"); // => true ```
枚举项可以手动赋值(仅可赋值为数字或字符串),未手动赋值的枚举项会接着上一个枚举项递增。
手动赋值的枚举项允许不是数字,此时需要使用类型断言<any>
来让编译器无视类型检查。
若紧接在不是数字的枚举项后面的是未手动赋值的项,则会因为无法获得初始值而报错。
e.g.
```typescript // .ts enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat ='S', a = 13, b} // 被编译为.js var Days; (function (Days) { Days[Days["Sun"] = 7] = "Sun"; Days[Days["Mon"] = 8] = "Mon"; Days[Days["Tue"] = 9] = "Tue"; Days[Days["Wed"] = 10] = "Wed"; Days[Days["Thu"] = 11] = "Thu"; Days[Days["Fri"] = 12] = "Fri"; Days[Days["Sat"] = 'S'] = "Sat"; Days[Days["a"] = 13] = "a"; Days[Days["b"] = 14] = "b"; })(Days || (Days = {})); ``` </details>
1
。计算所得项(computed member)
e.g.
```typescript // .ts enum Color {Red, Green, Blue = "blue".length, Yellow = 5, White} // 被编译为.js var Color; (function (Color) { Color[Color["Red"] = 0] = "Red"; Color[Color["Green"] = 1] = "Green"; Color[Color["Blue"] = "blue".length] = "Blue"; Color[Color["yellow"] = 5] = "yellow"; })(Color || (Color = {})); ```
常数枚举、外部枚举
与普通枚举的区别是:①在编译阶段被删除、②不能包含计算所得项、③若手动赋值则枚举值必须是数字。
常数枚举(Const Enums)
使用const enum
定义的枚举类型。
e.g.
```typescript // .ts const enum Directions { Up, Down, Left, Right } let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right] // 被编译为.js(不会生成枚举的变量) var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */]; ```
(外部)常数枚举 生成的.js,不会生成枚举的变量,而是直接把枚举的值硬编码出来。配置preserveConstEnums
可保持编译出枚举的变量。
外部枚举(Ambient Enums)
使用declare enum
定义的枚举类型。
e.g.
```typescript // .ts declare enum Directions1 { Up, Down, Left, Right } let directions1 = [Directions1.Up, Directions1.Down, Directions1.Left, Directions1.Right] // 被编译为.js var directions1 = [Directions1.Up, Directions1.Down, Directions1.Left, Directions1.Right]; ```
外部枚举 + 常数枚举
使用declare const enum
定义的枚举类型。
e.g.
```typescript // .ts declare const enum Directions2 { Up, Down, Left, Right } let directions2 = [Directions2.Up, Directions2.Down, Directions2.Left, Directions2.Right] // 被编译为.js(不会生成枚举的变量) var directions2 = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */]; ```
有静态方法的枚举
enum
+ namespace
e.g.
```typescript // .ts enum Weekday { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } namespace Weekday { export function isBusinessDay(day: Weekday) { switch (day) { case Weekday.Saturday: case Weekday.Sunday: return false; default: return true; } } } // const mon = Weekday.Monday; // const sun = Weekday.Sunday; // console.log(Weekday.isBusinessDay(mon)); // console.log(Weekday.isBusinessDay(sun)); // 被编译为.js "use strict"; var Weekday; (function (Weekday) { Weekday[Weekday["Monday"] = 0] = "Monday"; Weekday[Weekday["Tuesday"] = 1] = "Tuesday"; Weekday[Weekday["Wednesday"] = 2] = "Wednesday"; Weekday[Weekday["Thursday"] = 3] = "Thursday"; Weekday[Weekday["Friday"] = 4] = "Friday"; Weekday[Weekday["Saturday"] = 5] = "Saturday"; Weekday[Weekday["Sunday"] = 6] = "Sunday"; })(Weekday || (Weekday = {})); (function (Weekday) { function isBusinessDay(day) { switch (day) { case Weekday.Saturday: case Weekday.Sunday: return false; default: return true; } } Weekday.isBusinessDay = isBusinessDay; })(Weekday || (Weekday = {})); // const mon = Weekday.Monday; // const sun = Weekday.Sunday; // console.log(Weekday.isBusinessDay(mon)); // console.log(Weekday.isBusinessDay(sun)); ```
遍历枚举类型
in
e.g.
```typescript enum A { 'top', 'down', } function func() { for (var key in A) { console.log(key); } } func(); // => '0' => '1' => 'top' => 'down' ```
赋值兼容
枚举与数字类型相互兼容
e.g.
```typescript enum Status { Ready, Waiting } let status1 = Status.Ready; let num1 = 3 let num2 = 3 as const status1 = num1; status1 = num2; num1 = status1; num2 = status1 // Error, Type 'Status' is not assignable to type '1'. ```
来自于不同枚举的枚举变量,被认为是不兼容
e.g.
```typescript enum Status { Ready, Waiting } enum Color { Red, Blue } let status1 = Status.Ready; let color = Color.Red; status1 = color; // Error ```
泛型(Generics)
名称<多个类型名>使用部分
。泛型是指在定义函数(<多个类型名>(参数): 返回值
)、接口(interface 接口名<多个类型名>
)或类(class 类名<多个类型名>
)时,不预先指定具体的类型,而在使用时再指定类型的一种特性。
定义 - 赋值
定义:类型变量/泛型变量(用任意的非保留关键字)
当使用简单的泛型时,泛型常用
T
、U
、V
表示。若在参数里,不止拥有一个泛型,则应该使用一个更语义化名称,如TKey
、TValue
(通常情况下,以T
作为泛型的前缀,在其他语言如C++里,也被称为模板)。
赋值:传入类型,可以是自定义类型,可以是类型推论
e.g.
```typescript function func<T, P>(a: T, b: P, c: Array<T | P>) { return `${a} ${b} ${c}`; } func<string, number>("aaa", 222, ["a", "b", 3]); // 显式定义(不是类型推论) func<"aaa", 222>("aaa", 222, ["aaa", "aaa", 222]); // 显式定义(不是类型推论) func("a", 2, ["aa", 22]); // 类型推论。会报错,等价于:func<"a", 2>("a", 2, ["aa", 22]) func("a" as string, 2 as number, ["aa", 22]); // 类型推论 class A<T extends number | string> { constructor(private paras: T[]) {} } new A(["a1", "a2", "123"]); // 显式定义(不是类型推论) new A<number | string>(["a1", "a2", 123]); // 显式定义(不是类型推论) new A(["a1", 123]); // 类型推论 ``` </details>
泛型函数
e.g.
```typescript function identity(arg: T): T { return arg; } let myIdentity1: (arg: U) => U = identity; let myIdentity2: { (arg: T): T} = identity; let myIdentity3 = identity ; ``` </details>
泛型接口、泛型类型别名
e.g.
```typescript // 泛型接口1 interface GenericIdentityFn {(arg: T): T; } function identity (arg: T): T { return arg; } let myIdentity: GenericIdentityFn = identity; // 泛型接口2 interface GenericIdentityFn { (arg: T): T; } function identity (arg: T): T { return arg; } let myIdentity: GenericIdentityFn = identity; // 泛型类型别名 type LinkedList = { name: T; next: LinkedList }; var people: LinkedList ; var s = people.name; var s = people.next.name; var s = people.next.next.name; var s = people.next.next.next.name; ``` </details>
泛型类
类的静态属性不能使用泛型类型。
e.g.
```typescript class GenericNumber{ zeroValue: T; add: (x: T, y: T) => T = function (x, y) { return x; }; constructor(zeroValue: T) { this.zeroValue = zeroValue; } } let myGenericNumber = new GenericNumber (0); ``` </details>
没有泛型枚举、泛型命名空间。
<类型名 = 默认数据类型>
泛型约束:<类型名 extends 已有数据类型>
类型名 需要包含 已有数据类型。
e.g.
```typescript function loggingIdentity<T extends { length: number }> (arg: T): T { console.log(arg.length); return arg; } loggingIdentity(3); // 报错 loggingIdentity({length: 10, value: 3}); function getProperty<T, K extends keyof T> (obj: T, key: K) { return obj[key]; } getProperty({ a: 1 }, "a"); getProperty({ a: 1 }, "m"); // 报错 ```
内置类型别名
Partial
将类型定义的所有属性都修改为可选(非必须)。
e.g.
Partial<数据类型>
Required
将类型定义的所有属性都修改为必须(非可选)。
e.g.
Required<数据类型>
Readonly
将类型定义的所有属性都修改为只读(readonly)。
e.g.
Readonly<数据类型>
Record
将类型A的所有属性值都映射到类型B上并创造一个新的类型。
e.g.
```typescript type A = 'dog' | 'cat' | 'fish'; interface B { name: string, age: number, } type C = Record<A, B>; const c: C = { dog: { name: 'dogName', age: 2 }, cat: { name: 'catName', age: 3 }, fish: { name: 'fishName', age: 5 } } ```
Pick
从类型定义的属性中,选取指定一组属性,返回一个新的类型定义。
e.g.
```typescript interface A { title: string completed: boolean description: string } type someA = Pick<A, 'title'|'completed'> const a: someA = { title: 'Clean room', completed: false } ```
Omit
去除类型定义中的某些属性。
e.g.
```typescript interface A { title: string completed: boolean description: string } type AB = Omit<A, "completed"|"description"> const a: AB = { title: 'Clean room' } ```
Extract
从联合类型A中提取类型B。
e.g.
```typescript type T0 = Extract<'a' | 'b' | 'c', 'a' | 'b'> // -> 'a' | 'b' type T1 = Extract<string | boolean, boolean | number> // -> boolean type T2 = Extract<string | number , boolean> // -> never ```
Exclude
去除联合类型中的一部分。
e.g.
```typescript type a = number | string | boolean type b = Exclude<a, number | boolean> // -> string ```
NonNullable
去除联合类型中的null
和undefined
。
e.g.
```typescript type T1 = NonNullable<string | null | undefined>; // -> string type T2 = NonNullable<null | undefined>; // -> never ```
ReturnType
获得函数类型的返回类型。
e.g.
```typescript type F1 = () => Date; function F2 (): Date { return new Date() } type F1ReturnType = ReturnType; // -> Date type F2ReturnType = ReturnType ; // -> Date type F3 = ReturnType ; // -> number ``` </details>
Parameters
获取函数类型的全部参数类型,以元组
返回。
e.g.
```typescript type F0 = (a: string, b: number) => boolean; type F1 = Parameters; // -> [a: string, b: number] 或 [string, number] type F2 = F1[1]; // -> number type F3 = () => boolean; type F4 = Parameters ; // -> [] ``` </details>
InstanceType
获得构造函数类型的实例类型。
e.g.
```typescript class C { x = 0; y = 0; } type T0 = InstanceType; // -> C ``` </details>
ConstructorParameters
获取构造函数的全部参数类型,以元组
或数组返回。
e.g.
```typescript type T0 = ConstructorParameters; // -> [message?: string] type T1 = ConstructorParameters ; // -> string[] type T2 = ConstructorParameters ; // -> [pattern: string | RegExp, flags?: string] type T3 = ConstructorParameters ; // -> unknown[] type T4 = ConstructorParameters ; // -> never。报错 ``` </details>
ThisParameterType
获取函数类型中this
参数的数据类型,若没有则返回unknown
。
e.g.
```typescript function toHex(this: Number) { return this.toString(16); } function numberToString(n: ThisParameterType) { return toHex.apply(n); } ``` </details>
OmitThisParameter
移除函数类型中的this
参数的数据类型,返回移除后的函数类型。
ThisType
若使用,则需要开启
--noImplicitThis
。
Promise
Promise实例类型。
PromiseLike
仅有then
属性的对象类型,类似Promise实例的then属性(是Promise
类型的数据也满足PromiseLike
类型)。
操作固有字符串的类型(不是值)
这些类型内置于编译器之中,以便提高性能,它们不存在于
TypeScript提供的。.d.ts
文件中
Uppercase<StringType>
将字符串中的每个字符转换为大写字母。
Lowercase<StringType>
将字符串中的每个字符转换为小写字母。
Capitalize<StringType>
将字符串中的首字母转换为大写字母。
Uncapitalize<StringType>
将字符串中的首字母转换为小写字母。
e.g.
```typescript // Uppercase改成Lowercase、Capitalize、Uncapitalize,均可行 type Greeting = 'Hello, world' | 'Hi'; type ShoutyGreeting = Uppercase; // -> "HELLO, WORLD" | "HI" type ASCIICacheKey = `ID-${Uppercase }`; type MainID = ASCIICacheKey<'my_app'>; // -> "ID-MY_APP" ``` </details>
若没有明确的指定类型,则依照类型推论规则推断出一个类型。
最佳通用类型(从右向左)
声明时
any
。配置
noImplicitAny
:当无法推断一个变量(或只能推断为一个隐式的any
类型)时发出一个错误(显式添加any
不报错)。
上下文归类(从左向右)
若等号左侧已经确定了类型,则右侧的赋值也会吸收左侧的类型,并尝试约束自己的行为。通常包含:函数的参数,赋值表达式的右边,类型断言,对象成员,数组字面量,返回值语句。
<数据类型>变量名
或 变量名 as 数据类型
若JSX中使用
<数据类型>变量名
断言语法时,则与JSX的语法存在歧义(如:let foo = <string>bar;</string>
),因此在.tsx
中必须用变量名 as 数据类型
的断言语法。
可以绕过检查
双重断言
可以断言成任何类型:先断言成any
或unknown
,再断言成某类型。
任何类型都可以被断言为any
或unknown
,而any
和unknown
可以被断言为任何类型。
慎用双重断言。
e.g.
```typescript interface A { aa?: number } interface B { bb: number } let a: A = { aa: 2 }; let b: B = a as B; // 不报错 let c: B = a as any; // 不报错 let d: B = {} as B; // 不报错 let e: B = { aa: 2 } as B; // 报错 let f: B = ({ aa: 2 } as any) as B; // 不报错,双重断言 ```
const断言(const assertions)
as const
或<const>
,该表达式中:①字面量类型不会被扩展(如:不能从"hello"
转换为string
),②对象类型的属性成为只读字面量类型,③数组成为只读元组。
e.g.
```typescript const x1 = 'x1'; // -> 'x1' let x2 = 'x2'; // -> string let x3 = 'x3' as const; // -> 'x3' const obj1 = { key: 'value' } // -> { key: string } const obj2 = { key: 'value' } as const // -> { readonly key: 'value' } const arr1 = [1,2,3] // -> number[] const arr2 = [1,2,3] as const // -> readonly [1,2,3] ```
一些表达式,它们会在运行时检查以确保在块级作用域中获得更为精确的变量类型,从而减少不必要的类型断言,同时改善代码的可读性。
TS能够理解
if-else
、switch-case
等的逻辑分支下的类型变化。
类型判断
typeof 变量 !==或=== 值
e.g.
```typescript function test(input: string | number) { if (typeof input == 'string') { // 这里 input 的类型「收紧」为 string } else { // 这里 input 的类型「收紧」为 number } // 这里 input 仅能访问此联合类型的所有类型里共有的属性/方法 } ```
实例判断
变量 instanceof 构造函数
e.g.
```typescript class Foo {} class Bar {} function test(input: Foo | Bar) { if (input instanceof Foo) { // 这里 input 的类型「收紧」为 Foo } else { // 这里 input 的类型「收紧」为 Bar } // 这里 input 仅能访问此联合类型的所有类型里共有的属性/方法 } ```
属性判断
字面量 in 变量
e.g.
```typescript interface Foo { foo: string; } interface Bar { bar: string; } function test(input: Foo | Bar) { if ('foo' in input) { // 这里 input 的类型「收紧」为 Foo } else { // 这里 input 的类型「收紧」为 Bar } // 这里 input 仅能访问此联合类型的所有类型里共有的属性/方法 } ```
字面量相等判断
变量 !==或=== 字面量
e.g.
```typescript type Foo = 'foo' | 'bar' | 'unknown'; function test(input: Foo) { if (input != 'unknown') { // 这里 input 的类型「收紧」为 'foo' | 'bar' } else { // 这里 input 的类型「收紧」为 'unknown' } // 这里 input 仅能访问此联合类型的所有类型里共有的属性/方法 } ```
若上述条件不是直接通过字面量书写,而是通过一个条件函数来替代时,则类型保护便会失效
```typescript function isString (input: any) { return typeof input === 'string'; } function foo (input: string | number) { if (isString(input)) { // 这里 input 的类型没有「收紧」,仍为 string | number } else { // 这里也一样 } } ``` 1. 这是因为TS只能推断出`isString`是一个返回布尔值的函数,而并不知道这个布尔值的具体含义。 2. 并且JS并没有内置非常丰富的、运行时的自我检查机制。当你在使用普通的JS对象时,你甚至无法访问`instanceof`或`typeof`。 需要使用:自定义类型保护。
自定义类型保护
定义一个函数,它的返回值是一个类型谓词
:参数名 is 类型
。通过这个函数,可以缩小参数的类型范围。
本质是一种类型断言。
// 传进来参数的类型是`Fish | Bird`。函数返回`true`参数类型是`Fish`;函数返回`false`参数类型是除去`Fish`
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
// Both calls to 'swim' and 'fly' are now okay.
let pet = getSmallPet() as Fish | Bird;
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
|
若未赋值,则只能访问此联合类型的所有类型里共有的属性/方法(不确定联合类型的变量到底是哪个类型)。
类型断言 或 js逻辑判断(类型保护) 联合类型的变量成为联合类型其中的某一种类型,就可以访问此类型的属性/方法。
e.g.
```typescript function getLength(something: string | number) { something.toString(); // 访问此联合类型的所有类型里共有的属性/方法 (something as string).length; // 类型断言 (something).toFixed(); // 类型断言 if (typeof something === 'string') { something.length; // js逻辑判断(类型保护) } something.length; // 报错,只能访问此联合类型的所有类型里共有的属性/方法 ( something).toString(); // 报错,只能类型断言成一个联合类型中存在的类型 } ``` </details>
若已赋值,则只能访问类型推论出的某一个类型的属性/方法。
可辨识联合(Discriminated Unions)
可辨识(公共属性名) + 联合类型(|
) + 类型保护。
e.g.
```typescript // 可辨识:vType interface Motorcycle { vType: "motorcycle"; // discriminant make: number; // year } interface Car { vType: "car"; // discriminant transmission: 200 | 300; } interface Truck { vType: "truck"; // discriminant capacity: number; // in tons } // 联合类型 type Vehicle = Motorcycle | Car | Truck; function evaluatePrice(vehicle: Vehicle) { // 类型保护 switch (vehicle.vType) { case "car": return vehicle.transmission * Math.PI; case "truck": return vehicle.capacity * Math.PI; case "motorcycle": return vehicle.make * Math.PI; default: const invalidVehicle: never = vehicle; throw new Error(`Unknown vehicle: ${invalidVehicle}`); } } ```
第一个值前面也可以添加
|
(主要为了格式化美观)。e.g.type a = | number | string;
&
将多个类型合并为一个类型。
用法
number & string // -> never
{ 属性: 数据类型, } &
+
合并起来成为一个显式类型
e.g.
```typescript interface A { a: number; } interface B { b: string; } type C = { c: boolean; }; const aa: A & B & C & { d: number } = { a: 1, b: "", c: false, d: 1, }; ```
与起来成为一个显式类型
e.g.
type X = string & (1 | '2' | true) // -> '2'
{ 属性: 数据类型, } & 其他类型 // -> 可能没法直接给变量(可用于类型断言)
e.g.
{ 'x': {}, } & string
{ 属性: 数据类型1 } & { [key: string]: 数据类型2 } // -> 可能没法直接给变量,用于二次获取属性
表达式!
表示从前面的表达式(值,不是类型)里移除 和 null
。undefined
e.g.
```typescript // 配置文件:compilerOptions.strictNullChecks: true let foo: string | undefined foo.length // 报错, - 'foo' is possibly 'undefined' foo!.length function func (x: undefined | string): string{ return x! } ```
typeof 值
也保留js中的含义,但优先使用TS的语义。作为TS语法 比 作为JS语法,值支持的写法更少。
获取一个值(不是类型)的声明类型(或类型推论)。
e.g.
```typescript function foo(x: number): Array{ return [x]; } type F = typeof foo; // -> (x: number) => number[] class A { a: string; constructor() { console.log("I'm A"); } } const a: A = { a: "" }; type A2 = A; // -> A的实例类型 type A3 = typeof a; // -> a的类型( === A的实例类型) type A4 = typeof A; // -> A的类型 let a1: A2 = { a: "" }; let a2: A3 = { a: "" }; let a3: A4 = A; let a4: A4 = class B extends A { constructor() { super(); console.log("I'm B"); } }; new a3(); // => I'm A new a4(); // => I'm A I'm B ``` ```typescript const foo1 = 'Hello World'; let bar1: typeof foo1; // bar1 仅能被赋值 'Hello World' bar1 = 'Hello World'; // ok bar1 = 'anything else'; // Error let foo2 = 'Hello World'; let bar2: typeof foo2; bar2 = 'anything else'; // ok let foo3 = 'Hello World' as const let bar3: typeof foo3; // bar3 仅能被赋值 'Hello World' bar3 = 'Hello World'; // ok bar3 = 'anything else'; // Error ``` </details>
keyof 数据类型
(输入索引类型查询)获取某种数据类型的所有公共键(属性名),以联合类型(|
)返回。