- 类型保护(Type Guards)
- 可空类型(Nullable types)
- 类型别名(Type Aliases)
- 多态this
- 索引类型
- 映射类型
- 条件类型
- TS库中的内置类型转换实用类型
1.类型保护(Type Guards)
类型保护就是确保该类型在一定的范围内可以运行,简单来说就是当一个值为number类型或者string类型,类型保护就是当它为number类型时确保它的值为number,当它为string类型时确保它的值为sting
实现类型保护常用的有几下几种方法
- in
- instanceof
- typeof
- 自定义类型
class Bird {
name: string;
constructor(name: string) {
this.name = name;
}
fly() {
console.log("fly")
}
}
class Fish {
name: string;
constructor(name: string){
this.name = name;
}
swim(){
console.log("swim")
}
}
declare function getSmallPet(): Fish | Bird;
let pet = getSmallPet();
pet.name;
pet.swim(); //error 类型“Fish | Bird”上不存在属性“swim”
如上,假如当前pet确实是Fish类型的,我们想用到swim方法,我们就可以使用类型保护来确保typescript识别他的具体类型;
注: 当然我们可以使用类型断言 as,但是这不是我们想要的
使用 in
if("swim" in pet){
pet.swim(); //ok
}
使用 instanceof
if(pet instanceof Fish){
pet.swim() //ok
}
使用 typeof
注: typeof 只能判断该类型的基本类型,如"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
function getSum(arg: number | string): number{
if(typeof arg === "number"){
return arg++
}else{
return parseInt(arg) + 1
}
}
自定义的类型保护
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
if(isFish(pet)){
pet.swim();
}
2.可空类型(Nullable types)
TypeScript有两个特殊类型,null和undefined
let num: number | undefined = 123;
let obj: object | null = {};
// 此时求和就可以使用类型保护来避免出现错误
function getSum(){
if(typeof num === 'number'){
num++
}
}
3. 类型别名(Type Aliases)
类型别名即为类型创建新名称。比如为我们的接口数据定义一个类型Result:
type Result<T> ={
code: number;
data: T,
message: string
}
此时的Result即为类型别名
4.多态this
多态this类型是某个包含的类或接口的子类型;
this类型它能表现连贯接口间的继承简易性
class Base {
constructor(
public value: number
){}
add(num: number): this{
this.value += num;
return this;
}
multiply(num: number): this {
this.value *= num;
return this;
}
}
let a = new BaseCalculator(3);
a.add(7).multiply(5).add(5).value;//链式
使用this类型,可以对其进行扩展,并且新类可以使用旧类方法
//类可以拓展
class Child extends Base {
constructor(value: number = 0){
super(value);
}
public sin() {
this.value = Math.sin(this.value);
return this;
}
}
let b = new ScientificCalculator();
b.sin().add(1).multiply(4).value;
5.索引类型
索引类型即可被索引的类型,对应JavaScript 中的数组、对象等聚合多个元素的类型。
keyof 为常用的索引查询运算符
比如: 我们想获取某个对象中某个属性值
function getProperty<T, K extends keyof T>(o: T, propertyName: K): T[K] {
return o[propertyName];
}
interface People {
name: string;
age: number;
say(): void;
}
let li: People = {
name: "li",
age: 18,
say(){
console.log("i am li")
}
}
const liName = getProperty(li,"name");
6.映射类型
即类型可以映射,常用于基于旧类型创建新类型;
TS库中已内置映射方法有Partial,Readonly, Pick, Record
注:
Partial,Readonly, Pick为同态,即不会创建新属性 添加新成员只能用&
Record为非同态,即会创建新属性, 新属性由Record第一个属性指定
//将某类型全部变为可选属性
type Partial<T> = {
[P in keyof T]?: T[P];
};
//将某类型变为readonly类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
//抽取目标类型的子集类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
//非同态: 会创建新属性, 新属性由Record第一个属性指定
type Record<K extends keyof any, T> = {
[P in K]: T;
};
//eg:
interface People{
name: string,
age: number,
sex: string
}
type PartialMan = Partial<People>;//Partial
type ReadonlyMan = Readonly<People>;//Readonly
type PickMan = Pick<People, "name" | "age">;//Pick
let PartialLilei: PartialMan = {
name: "lilei",
age: 18
}
let PickLilei: PickMan = {
name: 'lilei',
age: 18
}
//同态添加新成员只能用&
type Woman= Readonly<People> & { say(): void};
//Record
enum method {
Get= 'get',
// Post= 'post',
// Delete= 'delete'
};
interface HttpFn {
(url: string, config?: any) : Promise<void>
}
type RecordHttp = Record<method, HttpFn>;
let httpget: RecordHttp = {
get: (url: string, config?: any) => {
//return new Promise((reslove,reject) => {
//reslove()
//})
},
post:(url: string, config?: any) => {
//return new Promise((reslove,reject) => {
//reslove()
//})
}
}
7 条件类型
根据条件判断来推测其类型。常用表达式为:
T extends U ? X : Y;
上面表达式含义为:若 T 能够分配(赋值)给 U(即是U的子类型),那么类型是 X,否则为 Y
type value<T> = T extends boolean ? string : number;
let val: value<false> = "123";//ok
let val: value<true> = 123; //error
条件延时推测---即当推测条件不足时,会推迟条件判断,直至条件充足时;
interface Foo {
propA: boolean;
propB: boolean;
}
declare function f<T>(x: T): T extends Foo ? string : number;
function foo<U>(x: U) {
// 因为 ”x“ 未知,因此判断条件不足,不能确定条件分支,推迟条件判断直到 ”x“ 明确,
// 推迟过程中,”a“ 的类型为分支条件类型组成的联合类型,string | number 联合类型
let a = f(x);
// 这么做是完全可以的
let b: string | number = a;
}
分布条件类型
即:其中选中的类型为裸类型参数的条件类型称为分布式条件类型;例化期间,分布条件类型自动分布在联合类型上。例如,T extends U ? X : Y
使用类型参数A | B | C
for的实例化T
解析为(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
。
注: 裸类型为 类型参数没有被包装在其他类型里,比如没有被数组、元组、函数、Promise,对象等包裹
如:
// 裸类型,没有被任何其他类型包裹,即T
type NakedType<T> = T extends boolean ? "YES" : "NO"
// 非裸类型,类型参数被包裹的在元组内,即[T]
type WrappedType<T> = [T] extends [boolean] ? "YES" : "NO";
条件类型的分布属性可以方便地用于过滤联合类型:
//过滤联合类型中的null和undefined,
type NonNullable<T> = T extends null | undefined ? never : T
//never 类型表示不会是任何值,即什么都没有
type a = string | number| Function | null | undefined;
let b: NotNullable<a> = 1;//ok
b = null; //error
条件类型与映射类型
条件类型和映射类型配合使用特别有用
//提取类型中属性值为Funtion类型key值,相当于 {key: key | never}[key]
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
//FunctionPropertyNames<T>为key值,Pick是提取属性值为FunctionPropertyNames<T>的值,相当于{key: T[key]}
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
//提取类型中属性值不为Funtion类型key值,相当于 {key: key | never}[key]
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
interface Part {
id: number;
name: string;
subparts: Part[];
updatePart(newName: string): void;
}
type T1 = FunctionPropertyNames<Part>;
//type T1 = "updatePart"
type T2 = FunctionProperties<Part>;
// type T3 = {
// updatePart: (newName: string) => void;
// }
type T3 = NonFunctionPropertyNames<Part>;
// type T2 = "id" | "name" | "subparts"
type T4 = NonFunctionProperties<Part>;
// type T4 = {
// id: number;
// name: string;
// subparts: Part[];
// }
8 TS库中的内置类型转换实用类型
1. Partial<Type> //将某类型全部变为可选属性,前面已介绍
2. Readonly<Type> //将某类型全部变为Readonly属性,前面已介绍
3. Pick<Type, Keys> //抽取目标类型的子集类型,前面已介绍
3. Record<Keys,Type> //非同态: 会创建新属性, 新属性由Record第一个属性指定 ,前面已介绍
4. Omit<Type, Keys> //移除某一类型中的属性
interface People{
name: string,
age: number,
sex: string,
height: number
}
type Man = Omit<People, 'height'>
/*
{ name: string,
age: number,
sex: string
}
*/
5. Exclude<Type, ExcludedUnion>//去除联合类型中指定类型
type T0 = Exclude<"a" | "b" | "c" | "d","a" | "b">;
// type T0 = "c" | "d";
6. Extract<Type, Union>//返回联合类型中相同类型
type T0 = Exclude<"a" | "b" | "c" | "d","a" | "f">;
// type T0 = "a";
7. NonNullable<Type>//去除类型中null和undefined;
type T1 = NonNullable<string[] | string | null | undefined>;
// type T1 = string[] | string;
8. Parameters<Type>//提取函数类型中参数类型,返回参数类型元祖,非函数返回unkown
type T0 = Parameters<() => string>;
// type T0 = []
type T1 = Parameters<(s: string) => void>;
// type T1 = [s: string]
type T2 = Parameters<any>;
// type T2 = unknown[]
type T3 = Parameters<never>;
// type T2 = never
9. ConstructorParameters<Type>//返回构造函数中Constructor参数元组
type T0 = ConstructorParameters<ErrorConstructor>;
// type T0 = [message?: string]
type T1 = ConstructorParameters<FunctionConstructor>;
// type T1 = string[]
type T2 = ConstructorParameters<RegExpConstructor>;
// type T2 = [pattern: string | RegExp, flags?: string]
type T3 = ConstructorParameters<any>;
// type T3 = unknown[]
10. ReturnType<Type>//返回函数的返回类型组成的类型。
type T0 = ReturnType<() => string>;
//type T0 = string
type T1 = ReturnType<(s: string) => void>;
//type T1 = void
type T2 = ReturnType<<T>() => T>;
// type T2 = unknown
11. InstanceType<Type>//返回构造函数的实例类型组成的类型
class C {
x = 0;
y = 0;
}
type T0 = InstanceType<typeof C>;
//type T0 = C
type T1 = InstanceType<any>;
// type T1 = any
type T2 = InstanceType<never>;
//type T2 = never
12. Required<Type>//与Partial相反,将类型中可选参数变为必选
interface Props {
a?: number;
b?: string;
}
const obj: Required<Props>;
//obj: {
// a: number;
// b: string;
//}
13.ThisParameterType<Type>//获取函数中 this的数据类型,如果没有则返回 unknown 类型
function toHex(this: Number) {
return this.toString(16);
}
function numberToString(n: ThisParameterType<typeof toHex>) {
return toHex.apply(n);
}
14.OmitThisParameter<Type>//移除函数中的 this 数据类型:
function toHex(this: Number) {
return this.toString(16);
}
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
console.log(fiveToHex());
15.ThisType<Type>//该实用程序不返回转换后的类型。相反,它用作上下文this类型的标记。请注意,--noImplicitThis必须启用该标志才能使用此实用程序
type ObjectDescriptor<D, M> = {
data?: D;
methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
};
function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
let data: object = desc.data || {};
let methods: object = desc.methods || {};
return { ...data, ...methods } as D & M;
}
let obj = makeObject({
data: { x: 0, y: 0 },
methods: {
moveBy(dx: number, dy: number) {
this.x += dx; // Strongly typed this
this.y += dy; // Strongly typed this
},
},
});
obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);
//在上面的例子中,`methods`在参数对象`makeObject`具有上下文类型,其包括`ThisType<D & M>`和因此的类型[此](https://www.typescriptlang.org/docs/handbook/functions.html#this)内的方法`methods`目的是`{ x: number, y: number } & { moveBy(dx: number, dy: number): number }`。请注意,`methods`属性的类型如何同时成为`this`方法中类型的推断目标和来源。
//该`ThisType<T>`标记接口是简单地宣布一个空的接口`lib.d.ts`。除了在对象文字的上下文类型中被识别之外,该接口的作用类似于任何空接口。