likes
comments
collection
share

「Typescript之旅」: 一文盘点Typescript中23个内置类型工具! (建议收藏)

作者站长头像
站长
· 阅读数 32

今日更文《一文盘点Typescript中23个内置类型工具! (建议收藏)》。

「Typescript之旅」: 一文盘点Typescript中23个内置类型工具! (建议收藏)

引言

    TypeScript 是一种基于 JavaScript 的类型编程语言, 提供了严格的类型检查机制和类型推导能力,类型是Typescript的核心与难点。

    在实际开发中,通过一个类型来创建另一个类型的需求并不少见。而Typescript给我们提供的内置类型工具可以帮助我们简化复杂的类型转换。本文将对ts内置的23个类型工具进行逐个解析。帮助大家在项目中更好的使用内置类型工具构造我们开发中想使用的类型!

01.Partial<Type>

作用:Partial接收一个泛型类型Type,并将Type所有属性都设置为可选的,返回构造的新类型。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例(更新部分资源):

interface User {
	name: string
	age: number
	address: string
}

function updateUser(user: User, fieldsToUpdate: Partial<User>) {
	return { ...user, ...fieldsToUpdate }
}

const user: User = { name: 'xiaoming', age: 30, address: '上海' }
const newUser = updateUser(user, { address: '北京' })

源码实现:

/**
 * Make all properties in T optional
 */
type Partial<T> = {
    [P in keyof T]?: T[P];
};

02.Required<Type>

作用:Required接收一个泛型类型Type,并将Type所有属性都设置为必选的,返回构造的新类型(Required的作用与Partial相反)。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例(验证输入数据):

interface Props {
	name?: string
	age?: number
}

function printProps(props: Required<Props>) {
	console.log(`${props.name} is ${props.age} years old.`)
}

printProps({ name: 'xiaoming'}) // error ❗️

「Typescript之旅」: 一文盘点Typescript中23个内置类型工具! (建议收藏)

源码实现:

/**
 * Make all properties in T required
 */
type Required<T> = {
    [P in keyof T]-?: T[P];
};

03. Readonly<Type>

作用:Readonly接收一个泛型类型Type,并将Type所有属性都设置为只读的,返回构造的新类型, 新类型的属性不可再进行分配。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例(对象初始化后防止其属性值被再次分配):

interface User {
  name: string
  age: number
}

const user: Readonly<User> = { name: 'xiaoming', age: 30 }
user.name = 'zhangsan' // error ❗️

「Typescript之旅」: 一文盘点Typescript中23个内置类型工具! (建议收藏) 源码实现:

/**
 * Make all properties in T readonly
 */
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

拓展:

  • 也通常与Object.freeze一起使用

    declare function freeze<Type>(obj: Type): Readonly<Type>;
    

04.Record<Keys, Type>

作用:构造一个对象类型,其属性键为Keys,属性值为Type

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例(创建具有一致性的字典):

interface User {
	name: string
	age: number
}

type UserName = 'xiaoming' | 'xiaohong' | 'xiaohuang'

const users: Record<UserName, User> = {
	xiaoming: { name: 'ming', age: 23 },
	xiaohong: { name: 'hong', age: 24 },
	xiaohuang: { name: 'huang', age: 25 }
}

源码实现:

/**
 * Construct a type with a set of properties K of type T
 */
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

05. Pick<Type, Keys>

作用: 从类型Type中选择一组属性Keys来创建类型。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例(从已有类型挑几个需要的属性构造新类型):

interface User {
  name: string;
  age: number;
  address: string
}

type NameAndAgeOnly = Pick<User, 'name' | 'age'>;
const nameAndAgeOnly: NameAndAgeOnly = { name: 'xiaoming', age: 26 };

源码实现:

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

扩展:

  • 结合pick函数使用
const user = {name: 'xiaoming', age: 26, address: 'shanghai'}
declare function pick<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K>;
const nameAndAgeOnly = pick(user, "name", "age"); // { name: string, age: number }
  • Pick类型的Keys泛型必须是在Type中存在的.

06. Exclude<UnionType, ExcludedMembers>

作用: 从联合类型UnionType中排除ExcludedMembers类型然后返回一个新类型。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例:

interface User {
  name: string;
  age: number;
  address: string
}

type UserExcludeAddress = Exclude<keyof User, 'address'> // "name" | "age"

源码实现:

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

扩展:

  • Exclude也可从对象联合类型中排出掉指定的选项
const user1 = { name: 'ming', age: 23, address: 'shanghai' } as const 
const user2 = { name: 'hong', age: 24, address: 'beijing' } as const 
const user3 = { name: 'huang', age: 25, address: 'shenzhen' } as const 

type Users = typeof user1 | typeof user2 | typeof user3

type UsersExcludeMing = Exclude<Users, { name: 'ming' }>

07. Extract<Type, Union>

作用: 从联合类型Type中提取Union类型然后返回一个新类型。

常用指数: ⭐️⭐️⭐️⭐️

使用场景示例:

interface User {
  name: string;
  age: number;
  address: string
}
type UserAddress = Extract<keyof User, 'address'>  // address

type Person = {
  name: string;
  age: string;
}
const user: Extract<keyof User, keyof Person> = 'name' || 'age';

type SuccessCode = Extract<200 | 404, 200>; // 200

源码实现:

/**
 * Extract from T those types that are assignable to U
 */
type Extract<T, U> = T extends U ? T : never;

扩展:

  • 如果泛型Type中没有Union, 返回never。
type SuccessCode = Extract<200 | 404, 204>; // never

08. Omit<Type, Keys>

作用: 与Pick相反,Omit是从Type中选取所有Keys属性然后删除构造一个新类型。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例:

interface User {
  name: string;
  age: number;
  address: string
}

type UserOmitAge = Omit<User, 'address'>;

const userOmitAge: UserOmitAge = {  name: 'xiaoming', age: 30 };

源码实现:

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

扩展:

  • 从源码可以看出来Omit的实现其实就是PickExclude的组合。因为Omit的使用场景比较多,所以Typescript应使用者要求新增了Omit工具类型。

09. NonNullable<Type>

作用: 通过从Type中排除nullundefined 来构造一个类型。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例:

type PortNumber = string | number | null;
type ServerPortNum = NonNullable<PortNumber>

源码实现:

/**
 * Exclude null and undefined from T
 */
type NonNullable<T> = T & {};

10. Parameters<Type>

作用: 接受一个函数类型, 将函数的参数处理成一个元组类型。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例:

function createStudent(sno: string, name: string, age: number) {
	return { sno, name, age }
}

type CreateStudentParams = Parameters<typeof createStudent>

const createStuParams: CreateStudentParams = ['112899022', 'ming', 30]
const stu1 = createStudent(...createStuParams)

源码实现:

/**
 * Obtain the parameters of a function type in a tuple
 */
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

扩展:

  • Parameters接收的泛型必须是一个函数类型,但是作为顶级类型any和底层类型never, Typescript对其有特殊的处理。

    type T1 = Parameters<any>; // unknown[]
    type T2 = Parameters<never>; // never
    
  • Typescript中定义的Function也不能被parameters接收,看Function 的实现就知道了。

    type T7 = Parameters<Function>; // error
    

    Function的实现:

    interface Function {
        readonly name: string;
    }
    
  • 如果函数是重载函数,那么返回的是最后一个函数的参数组成的元组类型

declare function stringOrNum(x: string): number;
declare function stringOrNum(x: number): string;
declare function stringOrNum(x: string | number): string | number;

type T1 = Parameters<typeof stringOrNum>; // [x: string | number]

11. ConstructorParameters<Type>

作用: 接受一个具有构造函数的类型, 将构造函数的参数处理成一个元组类型。

常用指数: ⭐️⭐️⭐️

使用场景示例:

class Test { constructor(a: number, b: string) {}}
type T1 = ConstructorParameters<typeof Test>; //  [a: number, b: string]
                                
type T2 = ConstructorParameters<new (x: string, y: number) => any> // [x: string, y: number]

源码实现:

/**
 * Obtain the parameters of a constructor function type in a tuple
 */
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;

扩展:

class ArticleModel {
	title: string
	content?: string
	constructor(title: string) {
		this.title = title
	}
}
class CreateInstance<T extends new (...args: any[]) => any> {
	private ClassConstructor: T
	constructor(ctr: T) {
		this.ClassConstructor = ctr
	}

	getInstance(...args: ConstructorParameters<T>): InstanceType<T> {
		return new this.ClassConstructor(...args)
	}
}

const articleCache = new CreateInstance(ArticleModel)
const amazonArticle = articleCache.getInstance('typescript实用类型')
  • ConstructorParameters也常用于获取内置构造方法或第三方库的参数类型

    type T3 = ConstructorParameters<ErrorConstructor> // [message?: string | undefined]
    type T4 = ConstructorParameters<FunctionConstructor> // string[]
    type T5 = ConstructorParameters<RegExpConstructor> // [pattern: string | RegExp, flags?: string | undefined]
    
  • anynever的特殊处理

    type T7 = ConstructorParameters<any>;
    type T8 = ConstructorParameters<never>;
    

12. ReturnType<Type>

作用: 获取函数类型的返回值类型。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例:

function getUser() {
	return { name: 'ming', age: 30 }
}

type User = ReturnType<typeof getUser>

const user: User = { name: 'hong', age: 26 }

源码实现:

/**
 * Obtain the return type of a function type
 */
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

扩展:

  • Parameters类型一样,接收的泛型必须是一个函数类型

  • 泛型是anynever的情况

    type T1 = ReturnType<any>; // any
    type T2 = ReturnType<never>; // never
    
  • Function不能被ReturnType接收

  • 如果函数是重载函数,那么返回的是最后一个函数的返回类型。

13. InstanceType<Type>

作用: 获取构造函数类型的返回类型(构造函数返回什么什么类型,InstanceType获取的就是什么类型)。

常用指数: ⭐️⭐️⭐️⭐️

使用场景示例:

class Person {
	constructor(public name: string) {}
}
type PersonInstance = InstanceType<typeof Person>
const person: PersonInstance = new Person('Alice')

interface User {
	new (name: string): Object
}
type UserInstance = InstanceType<User> // Object

源码实现:

/**
 * Obtain the return type of a constructor function type
 */
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;

扩展:

  • InstanceType也常用于获取内置构造方法或第三方库的参数类型

    type T3 = InstanceType<ErrorConstructor> // Error
    type T4 = InstanceType<FunctionConstructor> // Function
    type T5 = InstanceType<RegExpConstructor> // RegExp
    
  • anynever的特殊处理

    type T7 = InstanceType<any>;
    type T8 = InstanceType<never>;
    
  • 一个vue的例子

    <!-- MyModal.vue -->
    <script setup lang="ts">
    import { ref } from 'vue'
    
    const isContentShown = ref(false)
    const open = () => (isContentShown.value = true)
    
    defineExpose({
      open
    })
    </script>
    // 为了获得MyModal的实例类型,使用InstanceType实用程序提取其实例类型
    <!-- Other.vue -->
    <script setup lang="ts">
    import MyModal from './MyModal.vue'
    const modal = ref<InstanceType<typeof MyModal> | null>(null)
    const openModal = () => {
      modal.value?.open()
    }
    </script>
    

14. Awaited<Type>

作用: 获取Promise中的类型(如await、then方法返回的被Promise包裹的数据的类型)。适合处理异步操作并确保解析值的类型安全。

常用指数: ⭐️⭐️⭐️⭐️⭐️

使用场景示例:

type A = Awaited<Promise<string>>; // string
type B = Awaited<Promise<Promise<number>>>; // number

// 假如这是一个第三方库,User没有导出,fetchUser函数导出了
interface User {
	name: string
	age: number
}
export async function fetchUser(): Promise<User> {
	const data = await fetch('https://www.example.com/user').then(res => {
		return res.json()
	})
	return data
}

// 我们开发中在获取到了User类型
type UserFetch = Awaited<ReturnType<typeof fetchUser>>

async function getUserInfo() {
  let user: UserFetch = { name: 'ming', age: 30 }
  return user
}

源码实现:

/**
 * Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to `never`. This emulates the behavior of `await`.
 */
type Awaited<T> = T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode
    T extends object & { then(onfulfilled: infer F, ...args: infer _): any; } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
        F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument
            Awaited<V> : // recursively unwrap the value
        never : // the argument to `then` was not callable
    T; // non-object or non-thenable

扩展:

  • Awaited是可以递归解Promise

    type T1 = Awaited<Promise<Promise<Promise<Promise<Promise<number>>>>>> // number

  • 如果传入Awaited的泛型不是Promise,那么会原样返回

    type T2 = Awaited<string> // string
    

15. ThisParameterType<Type>

作用: 提取函数类型的this参数的类型, 如果函数类型没有this参数, 返回unknown。

常用指数: ⭐️⭐️⭐️

使用场景示例:

function toHex(this: Number) {
  return this.toString(16);
}
 
function numberToString(n: ThisParameterType<typeof toHex>) {
  return toHex.apply(n);
}

源码实现:

/**
 * Extracts the type of the 'this' parameter of a function type, or 'unknown' if the function type has no 'this' parameter.
 */
type ThisParameterType<T> = T extends (this: infer U, ...args: never) => any ? U : unknown;

16. OmitThisParameter<Type>

作用: 与ThisParameterType相反, 排除函数类型的this参数

常用指数: ⭐️⭐️⭐️

使用场景示例:

function toHex(this: Number) {
  return this.toString(16);
}
 
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
 
console.log(fiveToHex());

源码实现:

/**
 * Removes the 'this' parameter from a function type.
 */
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;

17. ThisType<Type>

作用: 控制字面量对象中this所表示的类型。 只在--noImplicitThis下有用

常用指数: ⭐️⭐️⭐️

使用场景示例:

// 正常情况推导出来的this
type Point = {
    x: number;
    y: number;
    moveBy(dx: number, dy: number): void;
}

let p: Point = {
    x: 10,
    y: 20,
    moveBy(dx, dy) {
        this.x += dx;  // this has type Point
        this.y += dy;  // this has type Point
    }
}

「Typescript之旅」: 一文盘点Typescript中23个内置类型工具! (建议收藏)

// 使用ThisType进行控制this
type Point = {
  x: number;
  y: number;
  moveBy(dx: number, dy: number): void;
}

let p: ThisType<{x: number}> &  Point = {
  x: 10,
  y: 20,
  moveBy(dx, dy) {
      this.x += dx;  // this has type Point
      this.y += dy;  // error ❗️类型“{ x: number; }”上不存在属性“y”。
  }
}

「Typescript之旅」: 一文盘点Typescript中23个内置类型工具! (建议收藏) 源码实现:

interface ThisType<T> {}

扩展:

对象字面值方法中的表达式this的类型按照下面的方法进行确定:

  1. 如果方法具有显式声明的this参数,则this具有该参数的类型。
  2. 否则,如果方法带了this参数,那么this就是这个。
  3. 否则,如果对象字面量有ThisType<T>的上下文类型,则该类型为T。
  4. 否则,如果不包含,那么就自动推导
  5. 否则,this就是any

18. ReadonlyArray<Type>

作用: 描述只能读的数组, 不可进行添加、删除、替换操作。

常用指数: ⭐️⭐️⭐️⭐️

使用场景示例:

function foo(arr: ReadonlyArray<string>) {
  arr.slice(); // okay
  arr.push("hello!"); //  Type error
  arr.pop(); // Type error
	arr.splice(1, 1); // Type error
  arr[0] = 'aa' // Type error
}
foo(['a','b','c'])

ReadonlyArray<string>的简写:readonly string[]

源码实现:

interface ReadonlyArray<T> {
	readonly length: number
	toString(): string
	toLocaleString(): string
	concat(...items: ConcatArray<T>[]): T[]
	concat(...items: (T | ConcatArray<T>)[]): T[]
	join(separator?: string): string
	slice(start?: number, end?: number): T[]
	indexOf(searchElement: T, fromIndex?: number): number
	lastIndexOf(searchElement: T, fromIndex?: number): number
	every<S extends T>(predicate: (value: T, index: number, array: readonly T[]) => value is S, thisArg?: any): this is readonly S[]
	every(predicate: (value: T, index: number, array: readonly T[]) => unknown, thisArg?: any): boolean
	some(predicate: (value: T, index: number, array: readonly T[]) => unknown, thisArg?: any): boolean
	forEach(callbackfn: (value: T, index: number, array: readonly T[]) => void, thisArg?: any): void
	map<U>(callbackfn: (value: T, index: number, array: readonly T[]) => U, thisArg?: any): U[]
	filter<S extends T>(predicate: (value: T, index: number, array: readonly T[]) => value is S, thisArg?: any): S[]
	filter(predicate: (value: T, index: number, array: readonly T[]) => unknown, thisArg?: any): T[]
	reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T): T
	reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T, initialValue: T): T
	reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: readonly T[]) => U, initialValue: U): U
	reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T): T
	reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T, initialValue: T): T
	reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: readonly T[]) => U, initialValue: U): U
	readonly [n: number]: T
}

19. Uppercase<StringType>

作用: 将字符串中的每个字符转换为对应的大写。

常用指数: ⭐️⭐️⭐️⭐️

使用场景示例:

type Greeting = 'Hello, world'
type ShoutyGreeting = Uppercase<Greeting> // "HELLO, WORLD"

type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<'my_app'> // "ID-MY_APP"

源码实现:

/**
 * Convert string literal type to uppercase
 */
type Uppercase<S extends string> = intrinsic;

21. Lowercase<StringType>

作用: 将字符串中的每个字符转换为对应的小写。

常用指数: ⭐️⭐️⭐️⭐️

使用场景示例:

type Greeting = 'Hello, world'
type QuietGreeting = Lowercase<Greeting> //  "hello, world"

type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`
type MainID = ASCIICacheKey<'MY_APP'> // "id-my_app"

源码实现:

/**
 * Convert string literal type to lowercase
 */
type Lowercase<S extends string> = intrinsic;

22. Capitalize<StringType>

作用: 将字符串中的第一个字符转换为大写字母。

常用指数: ⭐️⭐️⭐️⭐️

使用场景示例:

/**
 * Convert first character of string literal type to lowercase
 */
type Uncapitalize<S extends string> = intrinsic;

源码实现:

/**
 * Convert first character of string literal type to uppercase
 */
type Capitalize<S extends string> = intrinsic;

23. Uncapitalize<StringType>

作用: 将字符串中的第一个字符转换为小写字母。

常用指数: ⭐️⭐️⭐️⭐️

使用场景示例:

type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;

源码实现:

/**
 * Convert first character of string literal type to lowercase
 */
type Uncapitalize<S extends string> = intrinsic;

优秀的类型库

    我们应该都用过lodash,这是一个javascript版本的实用工具的库,能帮我们快速处理数据,在Typescript中有没有这样的静态类型的实用工具库呢?当然!

「Typescript之旅」: 一文盘点Typescript中23个内置类型工具! (建议收藏)

「Typescript之旅」: 一文盘点Typescript中23个内置类型工具! (建议收藏)

「Typescript之旅」: 一文盘点Typescript中23个内置类型工具! (建议收藏)

总结

Typescript提供的内置类型都是经过团队成员深思熟虑后添加的,通过提供的内置类型与组合内置类型可以覆盖广泛的使用场景。

往期文章

转载自:https://juejin.cn/post/7341669201009655845
评论
请登录