一. 基础数据类型
1. JS中的类型
TS包含了JS的八种数据类型,并且将它们都定义成基础数据类型。有以下几点需要注意:
null
和undefined
是任何类型的子集;所以在任何类型都可以被赋值为null
或undefined
。- 配置中设置
"strictNullChecks": true
。null
只能赋值给自身,undefined
只能赋值给void
和自身 number
和bigint
类型不兼容,不能直接互相赋值 ```typescript let str: string = ‘a’; let num: number = 2; let bool: boolean = false; let nul: null = null; let undef: undefined = undefined; let bigInt: bigint = 3n; let sym: symbol = Symbol(‘that’); let obj: object = {x: 1};
str = nul; // 只有”strictNullChecks”: false情况下可以 function v(): void{ return undefined; } // “strictNullChecks”: true情况下undefined只能赋值给undefined和void num = bigint // 不可以,两种数据类型不兼容
<a name="tGHHc"></a>
### 二. 其他类型
<a name="wfkG9"></a>
#### 1. 数组Array
<a name="O7EaG"></a>
##### 1) 定义数组类型的两种方式:
```typescript
let arr1: number[] = [1, 2, 3];
let arr2: Array<Number> = [4, 5, 6];
2) 定义联合类型的数组
联合类型的数组可以同时存储多种类型的数据
let uarr: (number | string)[] = [1, 'a', 2, 'b', 'c'];
3) 定义某种对象类型的数组
interface ArrType {
isMe: boolean;
name: string;
}
let tarr: ArrType[] = [
{
isMe: true,
name: 'jay'
},
{
isMe: false,
name: 'chou'
}
];
2. 函数
1) 函数声明
function funcDec (x: string, y: boolean): void {
return undefined;
}
2) 函数表达式
let funcExpr: (x: string, y: boolean) => void = function (x: string, y: boolean): void {
return undefined;
}
3) 接口定义的函数类型
interface IFunc {
(x: string, y: boolean): void;
}
let FuncObj: IFunc = function(x: string, y: boolean): void {
return undefined;
}
4) 可选参数
可选参数后面不能有必选参数
function optionalFunc(x: string, y: boolean, z?: number): void {
console.log(z)
}
optionalFunc("df", false); // z输出undefined
5) 默认参数
可以通过传入undefined
方式告诉函数这个参数不传值
function defaultFunc(x: string = 'fg', y: boolean):void {
console.log(x)
}
defaultFunc(undefined, false);
6) 剩余参数
function restFunc(x: string, y: boolean, ...items: string[]): void {
items.forEach((item) => {
console.log(item)
});
}
restFunc('dg', true, '43', 'hs');
7) 函数重载
函数重载是一种调用相同函数名,不同参数数量,类型和返回值类型的能力。调用的函数会依次匹配声明的函数
type combinable = string | number;
function reloadFunc(x: number, y: number): number;
function reloadFunc(x: number, y: string): string;
function reloadFunc(x: string, y: number): string;
function reloadFunc(x: string, y: string): string;
function reloadFunc(x: combinable, y: combinable): combinable {
if (typeof x === 'string' || typeof y === 'string') {
return x.toString() + y.toString();
}
return x + y;
}
3. 元组 Tuple
1) 声明与使用
使用下标访问
const tup: [number, boolean, string] = [1, false, 'gs'];
console.log(tup[1]) // false
2) 解构赋值
解构赋值的长度不能超过元组的长度,否则会报错
const tup: [number, boolean, string] = [1, false, 'gs'];
const [id, isMe] = tup; // 1 false
3) 可选类型
添加?
的形式添加可选参数。可选类型后面不能有必选类型
const tup: [number?, boolean?, string?] = [];
4) 剩余参数
剩余参数在可选类型后面
const tup: [number?, boolean?, ...number[]] = [1, false, 2, 3];
5) 只读元组
只读元组内部的值不能再改变
const tup: readonly [number?, boolean?, ...number[]] = [1, false, 2, 3];
tup[6] = 4; // 报错,因为是readonly,不能将undefined改变为4
4. void类型
void
类型和其他类型平级,一般表示没有返回值的函数的返回类型。不能直接赋值为undefeind
以外的值。
在strictNullChecks: false
时可以赋值为null
和undefined
let v1: void;
v1 = undefined;
function v(): void {};
5. never类型
never
类型代表永远不存在的值的类型;比如:
- 一个抛出错误的函数,它会中断函数;所以不存在返回值 ```typescript function throwFunc(): never { throw new Error(‘throw an error’); }
let bar: never = throwFunc();
2. 一个无限循环的函数
```typescript
function loopFunc(): never {
while(true);
}
let foo: never = loopFunc();
never
类型和undefined
,null
一样;是其他类型的子类型。但是除了never
类型的值,其他任何类型的值都不能赋值给never
,包括any
let a: any = 234;
let bar: never = a; // 报错,any类型不能赋值给never
应用场景:可以进行编译时全面的类型检查,排除联合类型中未实现的部分
type combinable = string | boolean | number;
function checkType(x: combinable): boolean {
if (typeof x === 'string') {
return true;
} else if (typeof x === 'boolean') {
return true;
} else {
const check: never = x; // number逻辑未实现,编译器报错
}
return false;
}
6. any类型
any
类型可以被赋值为任何类型,any
类型可以赋值给其他任何类型(never
除外);可以访问任意属性。
声明未指定类型,未赋值的变量默认为any类型
let defaultAn; // any类型
let an: any = {};
an.getType = '43';
an = 325
let str:string = an; // OK
let undef: undefined = an; //OK
let nul: null = an; // OK
let v: void = an; // OK
let nev: never = an; // Error never不可以被赋值为any
function nevFunc():never {
throw new Error('')
}
let an: any;
an = nevFunc(); // OK any可以被赋值为never
7. unknown类型
unknown
类型和any
一样都可以被任意类型赋值,但是unknown
类型只能赋值给unknown
和any
。除非进行了类型断言和类型判断;否则不能赋值给其他类型
function nevFunc():never {
throw new Error('')
}
let unk: unknown = '543';
let unk2: unknown = unk;
unk = false;
unk = nevFunc();
let an: any = unk;
let str: string = unk; // Error
如果不缩小类型,就不能对unknown
类型做任何操作
function getDog() {
return true;
}
let d: unknown = {
getDog
};
d.getDog(); // Error
(d as any).getDog(); // OK
8. Number String Boolean Symbol等包装对象类型
原始类型可以赋值给包装类型,包装类型不可以赋值给原始类型
let num: number = 1;
let Num: Number = 2;
Num = num; // OK
num = Num // Error
9. object,Object和{}
1) object
object
代表所有的非原始类型,不包含js中的基本数据类型string, number, boolean, symbol, bigint, null, undefined
let obj: object = {
a: 12
}; // OK
obj = new Date(); //OK
obj = 'dsf'; // Error
obj = 234; // Error
obj = false; // Error
obj = Symbol('324'); // Error
obj = 43543n; // Error
obj = null; // Error
obj = undefined; // Error
2) Object
Object代表所有有toString(), hasOwnProperty()
等方法的数据类型(编译器显示支持all objects
),所以所有的原始类型,非原始类型都可以赋值给它(null
和undefined
需要开启strictNullCheck
)
let Obj: Object;
Obj = {}; // OK
Obj = 1; // OK
Obj = 234n; // OK
Obj = 'gd'; // OK
Obj = false; // OK
Obj = Symbol('sdf'); // OK
Obj = null; // Error
Obj = undefined; // Error
Object
既是object
的父类型,也是object
的子类型。他们之间可以互相赋值
type is_obj_extends_Obj = object extends Object ? true : false; // true
type is_Obj_extends_obj = Object extends object ? true: false // true
let a: is_obj_extends_Obj = true;
let b: is_Obj_extends_obj = true;
3) {}
空对象的行为和Object
一致,表示原始类型和非原始类型的集合(null
,undefined
受strictNullCheck
影响)
let Obj: {};
function nevFunc(): never {
throw new Error();
}
Obj = nevFunc()
Obj = 1;
三. 类型推断
- 声明时赋值可以自动进行类型推断
- 函数返回值根据return判断
- 声明时未赋值,未添加类型的会被推断为
any
```typescript let str: string = ‘gsh’; // string
function add(a: number, b = 1) { return a + b; } // function add(a: number, b?: number): number
let an; // any类型 an = 324;
<a name="MYiDB"></a>
### 四. 类型断言
类型断言就是告诉编译器按照我的意思执行类型检查。只在编译阶段生效,并不作类型转换
<a name="KIP50"></a>
##### 1) 类型断言语法
类型断言有`as number`和`<number>`两种语法,其中`<number>`语法不与jsx兼容
```typescript
const arrNum: number[] = [1, 3 ,4];
const lagestNum1: number = (arrNum.find((num) => num > 3)) as number;
const lagestNum: number = <number>(arrNum.find((num) => num > 3));
2) 非空断言
非空断言就是断言一个变量类型不是null
和undefined
,用后置的!
表示
let combStr: null | undefined | string;
combStr!.toString(); // 断言combStr排除null和undefined
combStr.toString(); // Error 可能为null和undefined
type NumGen = () => number;
function assetFunc(numGen: NumGen | undefined) {
numGen!(); // 断言排除undefined
numGen(); // Error 可能为undefined
}
3) 确定赋值断言
明确告诉编译器变量会被赋值,类型检查的时候不会报未赋值的错误
let x!: number;
initFunc();
console.log(x * 2);
initFunc() {
x = 10;
}
五. 字面量类型
字面量可以作为变量的类型;字面量类型只有字符串字面量,数字字面量,布尔字面量类型三种。
let spe: 'this' = 'this';
let str: string = spe;
spe = str; // Error 字符串字面量类型'this'是string的子类型
字面量类型可以限制变量的类型和取值
interface Config {
dir: 'up' | 'down';
isWalk: false | true;
margin: 0 | 2 | 4;
}
let和const的类型推断其实也是一种类型拓展;
str2是字面量类型,它的父类型是string
,所以typeof str2 === 'string'
str是字面量类型,类型是'b'
,它的父类型是string
,所以被允许赋值给其他字符串字面量。推断出的类型就是字面量的父类型
let str = 'a';
const str2 = 'b';
六. 类型拓宽(Type Widening)
1. 普通的类型拓宽
类型拓宽指类型推断的时候编译器将变量的类型进行拓宽,推断为合理的类型
let str = 'this is a string' // 类型拓宽,string类型
let strFunc = (str = 'this is a string') => str // 类型拓宽 (str?: string) => string
const specStr = 'this is a string'; // 没有拓宽 'this is a string'类型
let str2 = specStr; // 类型拓宽 string类型
let strFunc2 = (str = specStr) => str; // 类型拓宽 (str?: string) => string
2. 特殊的类型拓展
null
和undefined
会自动拓宽为any
,变量有确定值的时候类型会收窄为特定的类型
let nul = null // any类型
let undef = undefined; // any类型
let nul2 = nul; // 收窄为null类型
interface Vector2 {
x: number;
y: number;
z: number;
}
function getComponent(vec: Vector2, axios: 'x' | 'y' | 'z') {
return vec[axios];
}
let vec = {x: 10, y: 20, z: 30 };
let x = 'x';
getComponent(vec, x); // x类型string,不能赋值给'x' | 'y' | 'z'类型
3. 限制类型拓展
1) 通过字面量类型限制类型拓展
let specStr2: 'this is a string' = 'this is a string';
let str3 = specStr2; // 限制类型拓展 类型为'this is a string'
2) const数据类型
对于原始类型,const可以限制为字面量类型
对于对象和数组,对象内部的类型会被认为是let声明的变量
const x = 'x' // 限制拓展'x'
const obj = {
x: 1,
y: 2
}; // 内部的属性会拓展 {x: number, y: number}
obj.x = 3; // OK
3) const 断言
const断言从值空间引入,将它收窄为最小的类型推断
let obj2 = {
x: 1,
y: 2 as const
} // { x: number, y: 2 }
let obj3 = {
x: 1,
y: 2
} as const; // { readonly x: 1, readonly y: 2 }
let arr = [1, 4] as const; // readonly[1, 4]
七. 类型收窄(Type Narrowing)
将宽泛的类型判断为一个比较明确的类型称之为类型缩小;
TS中用类型守卫来收窄类型的判断,编译器通过分析代码流判断类型。类型守卫是通过代码影响代码分析流的功能
function narrowFunc(varType: number | string): boolean {
if (typeof varType === 'number') {
varType; // number
return true;
}
varType; // string
return false;
}
一种常用的做法是通过标签联合或可辨识联合帮助接口类型收窄
interface TypeA {
type: 'A',
name: string
}
interface TypeB {
type: 'B',
age: number
}
type comb = TypeA | TypeB;
function combJudge(x: comb): string {
switch(x.type) {
case 'A':
return 'a';
case 'B':
return 'b';
default:
return 'ha'
}
}
注意:
不能通过
object
排除null
类型const el = document.getElementById('me');
if (typeof el === 'object') {
el; // null | HTMLElement
}
通过falsy值很多类型无法收窄
function falsyJudge(x: number | string | null | true) {
if (!x) {
x; // number | string | null 只排除了true
}
}
八. 联合类型
使用
|
创建联合类型,联合类型里可以是普通的类型或字面量类型;
如果联合类型中普通类型包裹了字面量类型,编译器只会提示普通类型let str: string | 'hi' = 'jay'; // string 这里是特殊的
let comb: number | 'h1' = 'h1'; // number | 'h1'
let huge: Object | string = true; // Object | string
let bigNum: 12n | 13 = 12n; // 12n | 13
九. 类型别名
给一个类型,联合类型取别名
type otherObj = Object;
type baseNum = 1 | 2 | 4; // 字面量类型也可以取类型别名
let str: otherObj = 'jskdh';
十. 交叉类型
交叉类型用
&
连接两个类型,原子类型取交集,非原子类型取并集
对于多个接口类型合并,不重名的原子属性取并集,重名的元素属性取交集,对象属性内部规则同理 ```typescript type obj = Object & object; // Object & object 收窄为非原始数据类型 let o: obj = {} // OK let o1: obj = 1; // Error 不符合object type str = ‘hi’ & string; // 收窄为’hi’ type useless = string & number; // never 原子类型没有交集
function nevFunc(): never {throw new Error()} interface ICat { id: string; age: number; feature: { miao: string } } interface IDog { id: number; age: number; isDog: boolean; feature: { wang: string } } type IPet = ICat & IDog; let pet: IPet = { id: nevFunc(), // 重名的原子类型取交集 age: 5, // 不重名的原子类型取并集 isDog: false, feature: { // 对象属性内部和外部同理,不重名合并,重名相交 miao: ‘miao’, wang: ‘wang’ } }
<a name="S1FC3"></a>
### 十一. 接口
TS中的接口可以是**对类部分行为的抽象**,也可以**描述对象的形状**。
<a name="p3TWB"></a>
#### 1. 接口描述对象形状,要严格一致,属性不能多也不能少
```typescript
interface ICat {
name: string;
age: number;
}
let c: ICat = { // Error 少了age属性
name: 'ksduhf'
};
let d: ICat = { // Error 多了isDog属性
name: 'dsg',
age: 43,
isDog: true;
};
2. 可选参数,只读参数
使用readonly
表明只读参数,可选只读参数也只能在初始化时赋值
interface ICat {
readonly isDog: boolean;
readonly isCat?: boolean;
name: string;
age?: number;
}
let cat: ICat = {
isDog: true,
name: 'cat'
};
cat.age = 32;
cat.name = 'rwe';
cat.isCat = true; // Error
cat.isDog = false; // Error
还有一个ReadonlyArray
,是一个只读的数组类型,不能修改数组内容
let rarr: ReadonlyArray<number> = [1, 3, 4];
rarr.push(4); // 不能修改
3. 任意参数
使用索引签名
为接口添加任意参数;任意参数只能有一个;任意参数类型必须包含其他参数的类型(可选参数多一个undefined
类型)
interface IAnimal {
isDog: boolean;
name: string;
age?: number; // 类型 number|undefined
[k: string]: number | string | boolean | null | undefined;
}
4. 类型兼容性
TS类型的兼容性是基于结构子类型的(也叫鸭式辨型法),结构类型是一种只使用其成员来描述类型的方法,结构类型的兼容性或等价性不要求显示的声明类型,它是基于类型的组成结构。和名义类型形成对比。
- 普接口,通对象的兼容性
如果x
兼容y
,则y
需要把包含x
的所有属性
interface IX {
name: string;
}
let x: IX;
let y = {
name: 'hi',
age: 23
};
x = y; // OK y有x所有成员
y = x; // Error x没有y所有成员
- 函数兼容性
参数类型:如果x
兼容y
,则x
需要包含y
所有参数类型且能对齐
let x = (x: string, y: number) => 0;
let y = (a: string) => 0;
x = y; // OK y的参数类型都能在x中找到
y = x; // Error x的参数类型number在y中找不到
返回值类型:如果x
兼容y
,则y
需要包含x
所有参数
let x = () => ({age: 13});
let y = () => ({name: 'jay', age: 12});
x = y; // OK y包含了x的所有参数
y = x; // Error x没有包含y的所有参数
- 类的兼容性
类型兼容只比较实例属性,不比较静态属性和构造函数X
要兼容Y
,则Y
需要有X
所有实例属性
class X {
jump: boolean = false;
constructor(name: string, jump: boolean){}
}
class Y {
jump: boolean = true;
constructor(){}
run() {
}
}
let x = new X('jay', false);
let y = new Y();
x = y; // OK y有x所有实例属性
y = x; // Error x没有y的run属性
5. 绕开额外属性检查的办法
1) 鸭式辨型法
长得像鸭子并嘎嘎叫的就是鸭子,这就是鸭子辨型法。下面的例子如果直接将{name: 'A', age: 23}
传入字面量对象会被类型检查限制。但是如果作为普通对象传入就会因为类型兼容不报错。
interface duck {
name: string;
}
function getDuck(d: duck): duck { return d }
let duckA: {name: string, age: number} = {
name: 'A',
age: 23
}
getDuck(duckA); // OK
getDuck({name: 'A', age: 23}) // Error
2) 类型断言 as
使用as告诉编译器自己知道在做什么,只要对象包含接口的属性就可以将对象断言为接口的实现
interface duck {
name: string;
}
function getDuck(d: duck): duck { return d }
getDuck({name: 'A', age: 23} as duck); // OK
3) 在接口定义的时候就是用索引签名
interface duck {
name: string;
[key: string]: any
}
function getDuck(d: duck): duck { return d }
getDuck({name: 'A', age: 23}); // OK
十二. 接口和类型别名的区别
二者都可用来声明对象和方法。
区别1:人如其名,类型别名可用于基本类型,联合类型,元组等取别名
type str = string;
type son = string | number;
type point = [number, number];
区别 2:interface,type可以拓展且可以相互拓展,interface使用extends
,type使用&
interface IPoint {
x: number;
y: number;
}
interface I3DPoint extends IPoint { z: number };
type TPoint = {
x: number,
y: number
}
type T3DPoint = TPoint & { z: number };
区别3:interface被重复声明会合并不报错,type重复声明会报错
interface IPoint {
x: number;
y: number;
}
interface IPoint {
isPoint: boolean;
}
// 变成{x: number, y: number, isPoint: boolean}
let p: IPoint = {
x: 1,
y: 2,
isPoint: true
}; // OK
十三. 泛型
泛型就是在定义函数,接口,类的时候不预先定义类型,而是在使用的时候再指定类型。
1. 泛型约束
如果需要泛型支持某个属性,就需要让这个泛型拓展某个接口<T extends someinterface>
interface IPoint {
x: number;
y: number;
}
function getDistance<T extends IPoint>(p: T): number { // 让p具有了x, y属性
return p.x + p.y;
}
2. 泛型工具类型
1) typeof
根据实例获取泛型类型,返回一个type。这个类型可以是推断出来的
interface IPoint {
x: number;
y: number;
}
let p:IPoint = {x: 1, y: 2};
type I = typeof p; // IPoint
let i: I = {x: 2, y: 3};
console.log(i)
// 支持推断出的类型
const cat = {
name: 'Kitty',
age: 3,
feature: {
isBig: false
}
};
type c = typeof cat;
/* 推断出的类型
{
name: string,
age: number,
feature: {
isBig: boolean
}
}
*/
const cat2: c = {
name: 'Jay',
age: 1,
feature: {
isBig: true
}
};
2) keyof
keyof返回一个类型,获取类型的所有键。只能用于类型
interface cat {
name: string;
age: number;
feature: {
isBig: boolean
}
}
type k = keyof cat;
const k1: k = 'feature' // OK 类型是"name" | "age" | "feature"
TypeScript支持字符串索引和数字索引,其中数字索引是字符串索引的子集。所以keyof
字符串索引得到的类型是string|number
interface cat {
[key: string]: string
}
const kit: cat = {
23: "hello"
}
type c = keyof cat; // string | number
interface cat {
[key: number]: string
}
const kit: cat = {
23: "hello"
}
type c = keyof cat; // number
应用场景:获取某个泛型对象的key
function getProps<T, K>(obj: T, key: K) {
return obj[key]; // Error
}
function getProps<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let res = getProps({a: 1, b: 'hi'}, 'b');