【TypeScript】内置条件类型约束的使用

87 阅读4分钟

背景

这两天接到了一个PC开发的任务,项目使用 ReactTypeScript 开发的,有很多 TypeScript 类型约束的写法,之前没有过多接触,这里记录一下相关学习内容。

TypeScript Playground

官网地址:www.typescriptlang.org/play/

在这里可以进行在线学习。

Pick(采集)

Pick 可以采集已定义对象中需要的一部分生成新的类型定义

// Pick 源码
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
interface IUser {
    name: string;
    age: number;
    sex: 0 | 1;
    address: string;
    weight: number;
}

type Person = Pick<IUser, 'name' | 'age'>;
/* 
    pick后类型:
    type Person = {
        name: string;
        age: number;
    }
*/ 

let person1: Person = { name: 'name', age: 1 } // 赋值正确
let person2: Person = { name: 'name', age: 1, sex: 0 } // 报错,Object literal may only specify known properties, and 'sex' does not exist in type 'Person'.

Partial(可选项)

Partial 可以把定义好的对象类型(必选+可选项)全部转化为 可选项

// Partial 源码
type Partial<T> = {
    [P in keyof T]?: T[P];
};
interface IUser {
    name: string;
    age: number;
    sex: 0 | 1;
    address: string;
    weight: number;
}

type Person = Partial<IUser>;
/* 
    Partial后类型:
    type Person = {
        name?: string | undefined;
        age?: number | undefined;
        sex?: 0 | 1 | undefined;
        address?: string | undefined;
        weight?: number | undefined;
    }
*/
let person1: Person = { name: 'name', age: 1 } // 赋值正确
console.log(person1.name, '==', person1?.name)

Required (必选)

RequiredPartial 的作用刚好相反,可把定义好的对象类型(必选+可选项)全部转化为 必选项

// Required 源码
type Required<T> = {
    [P in keyof T]-?: T[P];
};
interface IUser {
    name: string;
    age: number;
    sex: 0 | 1;
    address: string;
    weight: number;
}

type Person = Required<IUser>;
/* 
    Required后类型:
    type Person = {
        name: string;
        age: number;
        sex: 0 | 1;
        address: string;
        weight: number;
    }
 */
 
let person1: Person = {
    name: 'name',
    age: 1,
    sex: 0,
    address: "",
    weight: 0
} // 赋值正确
let person2: Person = { name: 'name'} // 报错, Type '{ name: string; }' is missing the following properties from type 'Required<IUser>': age, sex, address, weight

Readonly (只读)

Readonly 为对象类型中每一项添加前缀 Readonly

// Readonly 源码
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
interface IUser {
    name: string;
    age: number;
    sex: 0 | 1;
    address: string;
    weight: number;
}

type Person = Readonly<IUser>
/*
    // Readonly后类型:
    type Person = {
        readonly name: string;
        readonly age: number;
        readonly sex: 0 | 1;
        readonly address: string;
        readonly weight: number;
    }
 */
let person1: Person = {
    name: 'name',
    age: 0,
    sex: 0,
    address: "",
    weight: 0
} // 赋值正确
person1.name = 'name2' // 报错,Cannot assign to 'name' because it is a read-only property

Extract (提取/包括)

Exclude 用于处理联合类型,可以提取联合类型中的类型定义(只保留提取的联合类型)

// Extract实现源码 原理很简单
type Extract<T, U> = T extends U ? T : never;
// 联合类型
type TStringValue = '1' | '2' | '3'
type TNewStringValue = Extract<TStringValue, '1' | '2'> 
// 提取后类型为:type TNewStringValue = "1" | "2"

const obj1: TNewStringValue = '1'; // 赋值'1'正确
const obj2: TNewStringValue = '2'; // 赋值'2'正确
const obj3: TNewStringValue = '3'; // 报错,Type '"3"' is not assignable to type '"1" | "2"'.
interface IUser {
    name: string;
    age: number;
    sex: 0 | 1;
    address: string;
    weight: number;
}

// 对象联合类型
type Person = Extract<keyof IUser, 'name' | 'age'> // type Person = "name" | "age"

let person1: Person = 'name' // 赋值正确
let person2: Person = 'age' // 赋值正确
let person3: Person = 'sex' // 报错,Type '"sex"' is not assignable to type 'Person'.

Exclude (排除/不包括)

Exclude 用于处理联合类型,可以排除联合类型中的类型定义,作用和Extract 相反

// Exclude源码
type Exclude<T, U> = T extends U ? never : T;
type TStringValue = '1' | '2' | '3'
type TNewStringValue = Exclude<TStringValue, '1' | '2'> 
// 提取后类型:type TNewStringValue = "3"

const obj1: TNewStringValue = '1'; // 报错,Type '"1"' is not assignable to type '"3"'.
const obj2: TNewStringValue= '2'; // 报错,Type '"2"' is not assignable to type '"3"'.
const obj3: TNewStringValue = '3'; // 赋值正确
interface IUser {
    name: string;
    age: number;
    sex: 0 | 1;
    address: string;
    weight: number;
}

// 对象联合类型
type Person = Exclude<keyof IUser, 'name' | 'age'> // type Person = "sex" | "address" | "weight"

let person1: Person = 'sex' // 赋值正确
let person2: Person = 'address' // 赋值正确
let person3: Person = 'weight' // 赋值正确
let person4: Person = 'name' // 报错,Type '"name"' is not assignable to type 'Person'.

Omit(省略/剔除)

Omit 可以剔除已定义对象中不需要的部分生成新的类型定义

// Omit 的源码
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
interface IUser {
    name: string;
    age: number;
    sex: 0 | 1;
    address: string;
    weight: number;
}

// 剔除省略不需要的属性定义
type Person = Omit<IUser, "address" | "weight">;

let person: Person = {
    name: "",
    age: 0,
    sex: 0,
    // address: '111' // 报错,Object literal may only specify known properties, and 'address' does not exist in type 'Person'
}

ReturnType (类型推导返回)

ReturnType 可以推导函数类型并返回

// ReturnType 源码
type ReturnType<T extends (...args: any[]) => any> = T extends (
  ...args: any[]
) => infer R
  ? R
  : any;
function testUser() {
    return {
        name: 'name',
        age: 1,
        sex: 1
    }
}

type TypeUser = ReturnType<typeof testUser>; 
/*
    ReturnType推导函数的类型:
    type TypeUser = {
        name: string;
        age: number;
        sex: number;
    }
 */
let person: TypeUser = {
    name: "",
    age: 0,
    sex: 0
}

NonNullable (类型中排除 null 和 undefined)

NonNullable 用于联合类型,可以排除类型定义中为 nullundefined 的属性

// NonNullable 源码
type NonNullable<T> = T & {};
type MaybeString = string | null | undefined;  
let string1: MaybeString = null
let string2: NonNullable<MaybeString> = 'string1' // 正常赋值
let string3: NonNullable<MaybeString> = null // 报错,Type 'null' is not assignable to type 'string'.(2322)
let string4: NonNullable<MaybeString> = undefined // 报错,Type 'undefined' is not assignable to type 'string'.(2322)

使用场景

例如将定义类型 ExportIProps 转为 IProps

type ExportIProps = {
  title: string,
  column: Object[],
  placeholder: string,
  displayKey: Array<string>,
  sourceList: Object[], // 数据源
  showModal: boolean,
  onCancel?: () => void;
}

type IProps = {
  title?: string,
  placeholder?: string,
  sourceList: Object[], // 数据源
  showModal: boolean,
  onCancel?: () => void;
}
type ExportIProps = {
  title: string,
  column: Object[],
  placeholder: string,
  displayKey: Array<string>,
  sourceList: Object[], // 数据源
  showModal: boolean,
  search: (val: string) => void;
  sureAdd: (checkedList: Array<any>) => void;
  onCancel?: () => void;
}

const mapStateToProps = () => ({
  state: {
    name: 'nameState'
  },
});

type IProps = ReturnType<typeof mapStateToProps> 
    & Omit<ExportIProps, 'title' | 'column' | 'placeholder' | 'displayKey' | 'search' | 'sureAdd'> 
    & Partial<Pick<ExportIProps, 'title' | 'placeholder'>>

let p: IProps

image.png

参考

友情提示

见原文:【TypeScript】内置条件类型约束的使用)

本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。