前言
现在很多公司的项目在业务开发中都要求使用 TypeScript
来保证代码的可读性和质量, 但是不少人对TypeScript
的认知还比较浅薄,本文会介绍在TypeScript
中常用的操作符, 旨在帮助刚刚接触TypeScript
的开发人员能更深的认识以及更好的使用它
正文
Partial
Partial<T>
可以构造一个类型,将 T 的所有属性设为可选。 当你需要更新对象的部分属性时,Partial
非常有用。你可以创建一个新对象,只指定要更新的属性,并将其与原始对象合并, 如下例:
interface Person {
name: string
age: number
email: string
}
function updatePerson(person: Person, updates: Partial<Person>): Person {
return { ...person, ...updates }
}
const person: Person = { name: 'John', age: 30, email: 'john@example.com' }
const updatedPerson = updatePerson(person, { age: 31 })
Required
Required<T>
是 TypeScript 中的一个类型,它用于创建新类型,该类型中的所有属性都是现有类型的必需属性. Required
的作用与 Partial
相反,当你需要强制校验某对象必填时,可以使用该函数,如下例:
interface User {
username?: string
email?: string
}
function createUser(user: Required<User>) {
// 在此函数中,user 对象中的所有属性都是必需的,否则会产生类型错误
}
Readonly
Readonly<T>
用于创建一个新类型,该类型中的所有属性都是只读的,不能被修改。 有时你希望确保某些对象在被创建后不被修改。使用 Readonly
可以创建这样的对象类型,防止对象的属性被意外地修改, 如下例:
interface Point {
x: number
y: number
}
const point: Readonly<Point> = { x: 10, y: 20 }
point.x = 30 // Error: 无法分配到 "x" ,因为它是只读属性。
Record
Record<K, V>
的作用是创建一个对象类型,其中键的类型是 K
,值的类型是 V
。 它的常用场景就是当你需要快速创建一个键值对对象或者将一个类型的每个属性映射到另一个类型的值。 如下例
type Dictionary = Record<string, number>
const scores: Dictionary = {
alice: 95
}
// or
interface Fruit {
color: string
taste: string
}
type FruitInfo = Record<'apple' | 'banana' | 'orange', Fruit>
const fruitInfo: FruitInfo = {
apple: { color: 'red', taste: 'sweet' },
banana: { color: 'yellow', taste: 'sweet' },
orange: { color: 'orange', taste: 'sour' }
}
Pick
Pick<K, T>
的作用是从 T
中选择指定 K
(属性名)的属性,创建一个新类型。 当你需要从一个较大的类型中选择特定属性来创建一个新类型时,Pick
是一个非常有用的工具, 比如参数过滤,减少类型复杂度,如下例:
interface Product {
id: string
name: string
price: number
description: string
}
type ProductPreview = Pick<Product, 'id' | 'name' | 'price'>
function displayProductPreview(product: ProductPreview) {
console.log(`${product.name} - $${product.price}`)
}
Omit
Omit<T, K>
的作用是从 T
中排除指定的 K
,创建一个新类型。 它的使用场景大多是用于过滤或者排除属性,它与Pick
正好相反,在业务中的编辑中比较常用,如下例:
interface Product {
id: string
name: string
price: number
}
type ProductPreview = Omit<Product, 'id'>
function onCheckFormData(product: ProductPreview) {
console.log(`${product.name} - $${product.price}`)
}
Exclude
Exclude<T, E>
的作用是从 T
类型中排除所有在 E
联合类型中存在的类型. 它和 Omit
有点类似,它们的差别在于Exclude
用于排除类型,而 Omit
用于排除属性, 如下例:
interface Person {
name: string
age: number
gender: string
}
type NumberOrString = number | string
type OnlyString = Exclude<NumberOrString, number>
const str: OnlyString = 'Hello' // OK
const num: OnlyString = 42 // Error: 类型 "number" 不能分配给类型 "string"
NonNullable
NonNullable<T>
的作用是移除 T
类型中的 null
和 undefined
,创建一个新的非空类型, 这个使用场景大多在于移除一些包含空类型的对象,使用方法如下:
type Test = string | null
const e:NonNullable<Test> = null // 不能将类型“null”分配给类型“string”
Parameters
Parameters<F>
可以从函数类型中获取参数类型的元组类型。 使用示例如下:
function add(a: number, b: number): number {
return a + b
}
type Params = Parameters<typeof add>
const params: Params = [1,2]
add(...params)
ConstructorParameters
ConstructorParameters
用于从构造函数类型中获取参数类型的元组类型。 它的用途和 Parameters
相似,但是泛型接受不同,一个是函数, 一个是构造函数,使用方式如下:
class Person {
constructor(public name: string, public age: number) {}
}
// 获取 Person 构造函数的参数类型
type PersonConstructorParams = ConstructorParameters<typeof Person>
// 等同于 [string, number]
ReturnType
ReturnType
用于获取函数类型的返回值类型。 它在高阶函数或者函数管道中非常有用,如下例:
// 简单使用,定义一个函数类型
type Func = (a: string, b: number) => boolean
type FuncReturnType = ReturnType<Func> // 类型为 boolean
// 创建一个高阶函数,接受一个函数作为参数,并返回一个新函数
function wrapper<T extends (...args: any) => any>(
func: T
): (...args: Parameters<T>) => ReturnType<T> {
return (...args: Parameters<T>) => {
console.log('Wrapped function called')
return func(...args)
}
}
const wrappedFunc = wrapper((a: string, b: number) => a.length > b)
const result = wrappedFunc('hello', 3) // result 类型为 boolean
InstanceType
InstanceType
用于获取构造函数的实例类型。 最常见的情况就是 vue
中我们使用了某个组件,但是组件暴露出的方法和属性我们并不清楚,此时的ref
并不能得到很好的提示,由于 vue
组件本质就是一个函数实例,那么我们就可以使用 InstanceType
来获得该组件实例, 简单举例InstanceType
的用法:
class Person {
constructor(public name: string, public age: number) {}
}
// 使用 InstanceType 获取 Person 构造函数的实例类型
type PersonInstance = InstanceType<typeof Person> // 等同于 { name: string; age: number; }
通常在Vue
中获取组件expose的用法如下,首先我们先构建一个父子组件, 子组件中 expose 一些属性和方法,父组件通过ref去获取, 实例如下:
// Test.vue 中
<script setup lang="ts">
import { ref } from 'vue'
let inputValue = ref<string>('')
const onGetValue = () => {
return inputValue.value
}
defineExpose({ onGetValue })
</script>
<template>
<input type="text" v-model="inputValue">
</template>
父组件中通过TS的 InstanceType
声明类型来更好的获得提示:
<script setup lang="ts">
import { ref } from 'vue'
import Test from './components/Test.vue'
const testRef = ref<InstanceType<typeof Test> | null>(null)
const onRunGet = () => {
testRef.value?.onGetValue()
}
</script>
<template>
<Test ref="testRef" />
<button @click="onRunGet">run Test getValue func</button>
</template>
文末
随着 TypeScript
的日益普及,它为JavaScript
开发者提供了强大的类型系统,帮助我们构建更健壮、更易于维护的应用程序。希望今天的文章对你有帮助, 下一篇我会自己来实现 TS 的内置函数来帮助各位更好的去认识TS,如果感兴趣的话可以关注一下专栏或者作者