1. 类型
原始类型:Number,String,Boolean,null,undefined,Symbol
引用类型:Array,Function,Object
TypeScript在ES6的数据类型基础上,新增了以下数据类型:void, any, never, 元组,枚举,高级类型
-
null\undefined
- null 和 undefined 是所有类型的子集
- 被定义为null和undefined类型的,只能赋值为它本身
-
void
- void类型可以被赋值为undefined和null,但是不能被赋值其他的,比如string
-
数组
- number[]
- Array
interface INumberArray{ [index: number]: number; } -
元组
元组限定了数组的类型和个数,可以通过push越界放入,但访问不到
let a: [string, number] let b: ['666', 6666] -
any
可复制任意类型,不写相当于any
-
never
用不存在的值的类型。例如:总会抛出异常或不会有返回值的函数表达式
// 抛出异常 let error: never = (() => { throw new Error(); })() // 死循环 let endLess: never = (() => {while(true){}})() -
联合类型
let a = string | number;类型断言
let a = string | number; console.log((<string>a).length);类型别名
type newType = string | number let a: newType -
enum枚举类型
enum Gender {Male, Female} // {'0': 'Male', '1': 'Female', 'Male':0, 'Female':1} console.log(Gender['0']) // Male console.log(Gender['Male']) // 0数字枚举
enum Direction{ NORTH, // 默认0 SOUTH, // 默认1 EAST, // 默认2 WEST, // 默认3 } let dir: Direction = Direction.EAST; let dir = 4常量枚举
const enum普通枚举有对象,常量枚举没有对象,对象实体只存在于TS中,转译为ES5时删除用途:不需要该对象,只需要该对象的值时,适用常量枚举
// TypeScript 代码 const enum Gender {Male, Female} let a: Gender = Gender['Female'] console.log(a) // 1 // ES5 代码 var a = 1 /* 'Female' */; console.log(a);异构枚举:除了数字枚举,还可以字符串枚举,也可以两者混用
数字枚举比字符串枚举多了
反向映射
2. 接口
-
基础
interface IPerson{ name: string, age: number } -
可选属性
interface IPerson{ name: string, age?: number } -
只读属性
interface IPerson{ readonly name: string, age?: number } -
任意属性
interface IPerson{ name: string, age?: number, [propName: string]: boolean | string }
3. 函数
-
四种定义方式
- 函数声明
function add(x:number, y:number): number{ return x + y }- 函数表达式
const add = (x:number, y:number): number{ return x + y }- 别名
type addFunc = (x:number, y:number): number{ return x + y } let add: addFunc = (a,b) => a + b- 接口形式
interface IAdd{ (x: number, y: number): number } let add: IAdd -
可选参数
const add = (x:number, y?:number): number{ return x + (y || 0) } -
剩余参数
const add = (...args: number[]) => {} -
函数重载
只有一个函数,但可以对不同的参数组合进行静态类型检查
function aFun(arr: number[]) function aFun(arr: string[]) function aFun(arr: any[]) { if (typeof(arr[0]) === 'number') { return arr.reduce((pre, cur) => pre + cur) } else if (typeof(arr[0]) === 'string') { return arr.reduce((pre, cur) => pre + cur) } }
4. 类
- extends继承 - extends super
- static - 通过构造函数调用
- public - 类内外均可调用
- protected - 只能由类或子类的函数访问
- private - 只能由类本身的实例访问
- abstract
- 抽象类:不可实例化,不可直接new
- 抽象父类中的抽象方法,子类继承时需要实现该抽象方法
5. 类和接口的关系
-
类实现接口
- 接口声明成员属性和方法,不做实现,类声明并实现方法
- 同一个类可同时实现多个接口
interface IA{ a: number; f(x: number): void; } interface IA2{ b: string; } class A implements IA, IA2{ } -
接口继承接口
interface IA{ a: number; f(x: number): void; } interface IA2 extends IA{ b: string; } class A implements IA2{ } -
接口继承类
接口继承类,另一个类实现接口
class C1{ a: number; f(x: number): void; } interface I1 extends C1{ b: string; } class C2 implements I1{ }
6. 泛型
在定义函数、接口、类时,不预先指定具体类型,而是在使用时再指定类型。即使用时指定的类型在定义时的变量指代。
可提高代码的重用性
- 泛型函数
const a<T> = (a: T) : T => a const a<T, U> = (a: [T, U]): [T, U] => a; // 默认类型 const a<T = string> = (length: number, value: T>: Array<T> => {} - 泛型接口
实现接口时必须指定类型
interface Demo<T>{ (val: T): T } - 泛型类
class Demo<T> { value: T | undefined fn: ((x: T, y: T) => T) | undefined } - 泛型约束
interface Length{ length: number; } const a<T extends Length> = (a: T): T{}
7. 类型兼容
基于结构,只是用成员来描述类型
如果类型 x 要兼容 y, 那么 y 至少具有与 x 相同的属性。即 x 中的每个属性,在 y 中都能找到对应属性。(小类型兼容大类型)
interface X{
a: string;
}
let x: X = {a: '123', b: 123}
- 函数兼容
-
函数参数 协变:子类型赋值给父类型 逆变:父类型赋值给子类型
- 参数数量(协变)
重点:函数可忽略参数(传参多的兼容传参少的)
x赋值给y,需要确保x的每个参数在y里能找到对应类型的参数。let x = (a: number) => 0 let y = (b: number, s: string) => 0; y = x;- 参数类型(逆变)
重点:要保证有属性(属性少的兼容属性多的)
interface a{ name: string; age: number; } interface b{ name: string; } let foo = (x: a, y: number) => {]; let foo1 = (x: b) => {}; foo = foo1 -
函数返回值(协变)
-
- 枚举兼容
- 枚举类型与数字类型相互兼容
- 不同枚举类型之间不兼容
- 类兼容
- 仅比较类的实例属性
- 私有成员和受保护成员需来自同一个类
- 泛型兼容
- 作为类型参数,会影响适用其作为类型一部分的结果类型
8. 类型保护 / 类型守卫
在特定区块中保证变量属于某种确定的类型
- instanceof
- in
if('prop' in lang){
lang.prop()
}
- 类型保护函数
类型谓词:parameter is Type
class Java{
helloJava() {
console.log('Java')
}
}
class JavaScript{
helloJavaScript() {
console.log('JavaScript')
}
}
// 类型保护函数
function isJava(lang: Java | JavaScript): lang is Java { // 类型谓词
return (lang as Java).helloJava !== undefined
}
function typeGuardFunc(type: Type) {
let lang = type === Type.Strong ? new Java() : new JavaScript
if (isJava(lang)) {
lang.helloJava()
} else {
lang.helloJavaScript()
}
}
- typeof
- 字面量守卫
if(lang.kind === 'Java'){}
8. 高级类型
- 联合类型
- 交叉类型
多个类型的并集,将多个类型合并为一个类型
-
索引类型
- keyof type
- type['prop']
- 泛型约束
interface t{ a: number, b: string } let key: keyof t let value: t['a'] // 遍历对象 - 同时保持对象属性和属性值的类型约束 function pluck<T, K extends keyof T>(o: T, names: K[]){ return names.map(item => o[item]) } -
映射类型
从一旧类型生成新类型,实质为泛型接口
interface PersonPartial{ name?: string; age?: name; } type Readonly<T> = { readonly [P in keyof T]: T[P]; } type Partial<T> = { [P in keyof T]?: T[P] } type Keys = 'option1' | 'option2' type Flags = { [K in Keys]: boolean }in操作符内部适用了 for……in……
-
条件类型
type TypeName = T extends string ? 'string' : 'number';
- 联合类型 + 条件类型
<A | B> extends U ? X : Y
会转化为
(A extends U ? X | Y) | (B extends U ? X : Y);
9. 声明合并
- 接口合并
- 非函数成员
interface A{ height: number; } interface A{ width: string; }- 函数成员
- 合并,相当于函数重载
- 重载顺序:后来居上,接口内部按书写顺序
- 特殊优先顺序:参数的类型是单一的字符串字面量
interface A { foo(bar: number): number; // 5 foo(bar: 'a'): string; // 2 } interface A { foo(bar: string): string; // 3 foo(bar: string[]): string[]; // 4 foo(bar: 'b'): string; // 1 } // 合并后的等价效果 interface Cloner { foo(bar: 'b'): string; foo(bar: 'a'): string; foo(bar: string): string; foo(bar: string[]): string[]; foo(bar: number): number; }
10. 模版字面量类型
在模版字符串中使用extends
type StartsWith<Str extends string, Prefix extends string> =
Str extends `${Prefix}${string}` ? true : false;
11. 分配条件类型
使用条件类型时,如果左侧的参数为泛型类型,且传入的该参数为联合类型,条件类型将会作用到该联合类型的每一个成员。
类似于使用分配率,将联合类型拆分为单项,分别代入条件类型,最后将各单项的代入得到的结果联合作为最终结果。
type StrOrNum = string | number
type Result1 = StrOrNum extends string ? string : number // number
type Generic<T> = T extends string ? string : number
// 使用泛型,泛型参数为联合类型
type Result2 = Generic<StrOrNum> // string | number
// 等价于
type Type1 = Generic<string>
type Type2 = Generic<number>
type Result3 = Type1 | Type2 // string | number
若要避免这种行为,可以用方括号将 extends 关键字的每一侧括起来。
// 不希望使用分配条件类型
type Generic2<T> = [T] extends [string] ? string : number;
type Result4 = Generic2<StrOrNum> // number
12. infer
在extends条件类型,还可以使用infer关键字,可以用于推断一个类型变量,但该变量只能在条件类型的true分支上使用。
type ItemType<T> = T extends (infer R)? R : never;
type temp1 = ItemType<number[]> // number
type temp2 = ItemType<string[]> // string
type temp3 = ItemType<[1,2,3]> // 1 | 2 | 3
可在一个条件类型表达式中推断多个类型变量
type FuncType = (a: string, b: number) => boolean
type FuncValueAndReturn<T extends function> =
T extends (a: infer A, b: infer B, ...args: any[]) => infer C ? [A, B, C]: never
type result = FuncValueAndReturn<FuncType> // [string, number, boolean]
在一个条件类型表达式中多处使用同一个类型变量
- 协变 在协变位置上,同一个类型变量的多个候选类型会被推断为联合类型
type ItemType2<T extends any[]> = T extends [infer R, infer R, ...any[]] ? R : never;
type temp2 = ItemType2<[number, string]> // string | number
type temp2 = ItemType2<[1, 2, 3]> // 1 | 2
- 逆变 在逆变位置上,同一个类型变量的多个候选类型会被推断为交叉类型
type FuncType = (a: string, b: number) => boolean
type FuncValueAndReturn2<T extends function> =
T extends (a: infer A, b: infer A) => infer C ? [A, C] : never;
type result2 = FuncValueAndReturn2<FuncType> // [string & number, boolean]
在模板字符串中使用infer
type ReplaceStr<
Str extends string,
From extends string,
To extends string
> = Str extends `${infer Prefix}${From}${infer Suffix}`
? `${Prefix}${To}${Suffix}` : Str;
type ReplaceResult = ReplaceStr<'javascript','java','type'>