数据类型
基础数据类型
js
基础数据类型
// 不标注类型, 也会自动去做类型推断
let str: string = "jimmy";
let num: number = 24;
let bool: boolean = false;
let u: undefined = undefined;
let n: null = null;
let obj: object = {x: 1};
let big: bigint = 100n;
let sym: symbol = Symbol("me");
ts
新增类型
/**
* 任何类型的值可以赋值给any,同时any类型的值也可以赋值给任何类型
* 任何类型的值都可以赋值给unknown,但 unknown只能赋值给unknown 和 any
*/
// unknown --- 所有类型都可以赋值给 unknown
let un: unknown;
// any --- 所有类型都可以赋值给 any
let val: any;
/**
* never 表示的是那些永不存在的值的类型
* never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型
*/
// 异常
function error(msg: string): never { // 编译正确
throw new Error(msg);
}
// 死循环
function loopForever(): never { // 编译正确
while (true) {};
}
// key不存在
type A<V, T> = {
[K in keyof V as V[K] extends T ? K : never]: V[K];
};
数组
let arr1: number[] = [1, 2, 3, 4]
let arr2: Array<string> = ['1', '2', '3', '4']
interface ArrayNumber {
[index:number]: string
}
let arr3: ArrayNumber = ['a', 'b']
let arr4: (number | string)[] = ['1', 2]
let arr5: Array<number | string> = [1, '2']
/**
* 元组 tuple --- 特殊数组
*
* 元组类型只能表示一个已知元素数量和类型的数组,长度已指定,越界访问会提示错误
*
* 可以解构赋值
*
* 可以定义可选
*
* 定义剩余元素
*
* readonly控制只读
* */
let tuple: readonly [string, number, number?, ...string[]];
tuple = ['hello', 10];
tuple = ['hello', 10, 10];
tuple = ['hello', 10, 10, '10'];
// 解构
let [name, age] = tuple;
// 设置了 readonly 修改值 --- error
/* tuple[0] = '2'
tuple.push('3'); */
对象
{}
、Object
用来表示 原始类型
(null、undefined 除外)和 非原始类型
object
则表示 非原始类型
。
object
object
类型用于表示所有的 非原始类型
,即我们不能把 number、string、boolean、symbol等 原始类型赋值给 object
。在严格模式下,null 和 undefined 类型也不能赋给 object
。
let object: object;
object = {}; // ok
object = 1; // 报错
object = "a"; // 报错
object = true; // 报错
object = null; // 报错
object = undefined; // 报错
Object / {}
Object
或者 {}
代表所有拥有 toString
、hasOwnProperty
方法的类型。
所有原始类型、非原始类型 都可以 赋给 Object
(严格模式下 null 和 undefined 不可以)
let bigObject: Object;
object = 1; // ok
object = "a"; // ok
object = true; // ok
ObjectCase = {}; // ok
object = null; // 报错
ObjectCase = undefined; // 报错
函数
// 函数声明
function sum(x:number, y:number):number {
return x + y
}
let sum1 = (x:number, y:number): number => {
return x + y
}
// 函数表达式
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
// 接口定义函数
interface SearchFunc {
(source: string, subString: string): boolean;
}
const SearchFuncFn: SearchFunc = (source: string, subString: string): boolean => {
return false
}
/**
* 可选参数
* 参数默认值
* 剩余参数
*/
const handleFn = ( a: string, b?: string ) => {
return a + b
}
const handleFn2 = ( a: string, b: string = '4' ) => {
return a + b
}
const handleFn3 = ( a: string, ...str: string[] ) => {
return a + str.join(',')
}
// 定义类型
interface SetPointIfc {
(x: number, y: number): void;
}
type SetPointType = (x: number, y: number) => void;
let fn1: SetPointIfc = (x, y) => {}
let fn2: SetPointType = (x) => {}
// error
// let fn3: SetPointType = (x: string) => {}
类型断言
// 尖括号 语法
let str1: any = "this is a string";
let strLength: number = (<string>str1).length;
// as 语法
let str2: any = '123456';
let numberLength: number = (str2 as string).length;
const mtCanvas = document.getElementById('canvas') as HTMLCanvasElement
const mtCanvas2 = <HTMLCanvasElement>document.getElementById('canvas')
// unknown 不知道(不确定)为什么类型
const x = (123 as unknown) as string
/**
* 非空断言
*
* ! 仅用于断言 它不是 null 和 undefined
*/
function myFunc(str?: string | null) {
// const num1 = str.length; // error - 'str' is possibly 'null' or 'undefined'
const num2 = str!.length;
const num3 = str?.length;
if (str) {
const num4 = str.length;
}
}
let num1: number
// error - Variable 'num1' is used before being assigned
// let sum1 = num1 * 2 // 还没赋值前使用了
let num2!: number
let sum2 = num2 * 2
let userInput: unknown;
let userName: string;
userInput = 5;
userInput = "Hello";
// 需要进行类型检查或类型断言
if (typeof userInput === "string") {
userName = userInput; // 这里可以赋值,因为已经进行了类型检查
}
联合 / 交叉 - 类型
联合类型
- 联合类型表示取值可以为多种类型中的一种,使用
|
分隔每个类型 - 通常与
null
或undefined
一起使用
let variable: string | number;
variable = 'seven';
variable = 7;
// 参数联合类型
const fn = (str: string | null | undefined) => {
}
// 值就是类型
let num: 1 | 2 = 1;
type EventNames = 'click' | 'scroll' | 'mousemove' | '';
交叉类型
- 交叉类型是 将多个类型合并为一个类型,使用
&
定义交叉类型 - 交叉类型真正的用武之地就是将多个接口类型合并成一个类型,从而实现等同接口继承的效果,也就是所谓的合并接口类型
- 同名属性的类型不兼容,会变成
never
// never - 不可能满足
type Useless = string & number;
type PeopleType = { id: number; name: string; } & { age: number };
const mixed: PeopleType = {
id: 1,
name: 'name',
age: 18
}
/*
如果同名属性的类型兼容,比如一个是 number,另一个是 number 的子类型、数字字面量类型,
合并后 name 属性的类型就是两者中的子类型。
*/
type IntersectionTypeConfict = { id: number; name: 2; }
& { age: number; name: number; };
let mixedConflict: IntersectionTypeConfict = {
id: 1,
name: 2,
age: 2
};
mixedConflict = {
id: 1,
// name: 22, // '22' 类型不能赋给 '2' 类型
name: 2,
age: 2
};
// 同名属性是非基本数据类型的
interface A {
x:{d:true},
}
interface B {
x:{e:string},
}
interface C {
x:{f:number},
}
type ABC = A & B & C
let abc:ABC = {
x:{
d:true,
e:'',
f:666
}
}
类型别名
类型别名, 仅仅是给类型取了一个新的名字, 并不是创建了一个新的类型**
type Point = {
x: string,
y: number
}
function fnc1(params: Point) {
}
fnc1({
x: '1',
y: 1
})
type ID = string | number
let card:ID
card = '123'
card = 123
type Event = 'click' | 'scroll' | 'mousemove'
let handleEvent: Event = 'click'
/**
* Type 不能同名
* 使用 & 扩展继承合并
*/
type C = {
name: string
}
type D = C & {
sex: boolean
}
let user1: D = {
name: '张三',
sex: true
}
接口
可用于[ 对类的一部分行为进行抽象 ]以外,也常用于对「 对象的形状(Shape)」进行描述
定义
/* 定义的必须属性,不允许多,也不允许少 */
interface Person {
readonly id: string; // 只读
name: string;
}
let zs: Person = {
id: '1',
name: '张三'
}
// 修改只读属性 - error
// zs.id = '2' // Cannot assign to 'id' because it is a read-only property
可选属性
/* 可选属性 */
interface Person2 {
id: string;
name?: string; // '?' 表示非必须
}
let zs2: Person2 = {
id: '1'
}
任意属性
/* 可添加任意属性 */
interface Person3 {
name: string;
// 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
// [propName: string]: string;
age?: number; // 他需要是 任意属性的 子类型 --- 允许是 string
// 一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型
[propName: string]: string | number | undefined | null
}
let zs3: Person3 = {
name: 'Tom',
age: 25,
gender: 'male'
};
合并 / 扩展
/**
* 合并: interface重名合并
* 扩展: 通过extends继承
*/
interface E {
name: string
}
interface E {
sex: boolean
}
let user: E = {
name: '张三',
sex: false
}
// 继承一个接口直接 extends
interface F extends E {
age: number
}
// 继承多个接口用 (逗号 ,) 隔开
// 同样的, 他可以被反复 extends
interface G extends E, F { }
let user2: F = {
name: '张三',
sex: false,
age: 18
}
类型辨定
/**
* 类型辨定 --- 鸭式辨型法
* */
interface LabeledValue {
label: string;
}
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
/* 在参数里写对象就相当于是直接给labeledObj赋值,这个对象有严格的类型定义,所以不能多参或少参。 */
// error
// printLabel({ size: 10, label: "Size 10 Object" });
/*
当在外面将该对象用另一个变量myObj接收,myObj不会经过额外属性检查,
但会根据类型推论为let myObj: { size: number; label: string } = { size: 10, label: "Size 10 Object" };
然后将这个myObj再赋值给labeledObj,
此时根据类型的兼容性,两种类型对象,参照鸭式辨型法,
因为都具有label属性,所以被认定为两个相同,故而可以用此法来绕开多余的类型检查。
*/
// OK
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
枚举
- 只读,不能修改。 — 可以当作一个对象当参数传递使用
- 无默认值, 从0开始依次递增
- 指定初始值,依次递增
- 只要枚举有一个是字符串, 所有成员全部都需要 赋值
enum arr {
one,
two,
three = 5,
four
}
console.log(arr.one); // 0
console.log(arr.two); // 1
console.log(arr.three); // 5
console.log(arr.four); // 6
// 只要枚举有一个是字符串, 所有成员全部都需要 赋值
enum userStoreEnum {
TOKEN = 'token',
USER_INFO = 'userInfo',
ROLE = 'roles',
}
interface UserInfoType {
readonly id: string
name: string
}
// 通过 userStoreEnum[key]获取枚举值
interface UserStoreType {
[userStoreEnum.TOKEN]: string
[userStoreEnum.USER_INFO]: UserInfoType | null
[userStoreEnum.ROLE]: string[]
}
泛型
使用前不确定类型, 使用时才确定类型, 将 使用的类型传入**
常用字母
— 可以用任何有效名称代替
T
:Type
,在定义泛型时通常用作第一个类型变量名称K
:Key
,对象key类型V
:Value
,对象value值类型E
:Element
,表示元素类型P
:P in K
U
:Type<T, U>
// T 是一个抽象类型,只有在调用的时候才确定它的值
function identity<T>(arg: T): T {
return arg;
}
identity(1) // T的类型是 1
identity<number>(1) // T的类型是 number
// 可以使用任意个数的泛型
function fn<T, U>(value: T, message: U) : {value: T, message: U} {
return {value, message}
}
// 会自动进行类型推断
fn(68, "Semlinker")
fn<Number, string>(68, "Semlinker")
// 泛型约束 --- 通过 extends 指定这个泛型必须要拥有哪些属性
interface Sizeable {
size: number;
}
function trace<T extends Sizeable>(arg: T): T {
console.log(arg.size);
return arg;
}
工具
typeof
获取变量, 属性, 函数的类型
interface Person {
name: string;
age: number;
}
const sem: Person = { name: "semlinker", age: 30 };
type Sem = typeof sem; // type Sem = Person
type NameType = typeof sem.name
const message = {
name: "jimmy",
age: 18,
address: {
province: '四川',
city: '成都'
}
}
type Message = typeof message;
function toArray(x: number): Array<number> {
return [x];
}
type Func = typeof toArray; // -> (x: number) => number[]
keyof
获取某种类型的所有键,返回类型是联合类型。
interface Person {
name: string;
age: number;
}
type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
type K3 = keyof { [x: string]: Person }; // string | number
let k1: keyof boolean; // let K1: "valueOf"
let k2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let k3: keyof symbol; // let K1: "valueOf"
// 使用情况
type Todo = {
id: number;
text: string;
done: boolean;
}
const todo: Todo = {
id: 1,
text: "Learn TypeScript keyof",
done: false
}
function prop<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const id = prop(todo, "id");
const text = prop(todo, "text");
const done = prop(todo, "done");
// error
// const other = prop(todo, "other");
in
用来遍历枚举类型。
type Keys = "a" | "b" | "c"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any, c: any }
infer
在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用。
用infer声明一个变量名,占用原有位置的泛型类型
type ReturnType<T> = T extends (
...args: any[]
) => infer R ? R : any;
例:
// 获取数组第一个类型
type Head <T extends any[]> = T['length'] extends 0 ? never : T[0];
type Head <T extends any[]> = T extends [head : infer H, ...rest : any[]] ? H : never;
type H0 = Head<[]> // never
type H1 = Head<[1]> // 1
type H2 = Head<[3, 2]> // 3
type H3 = Head<["a", "b", "c"]> // "a"
type H4 = Head<[undefined, "b", "c"]> // undefined
type H5 = Head<[null, "b", "c"]> // null
// 用于获取数组类型除了第一个类型外,剩余的类型。
type Tail<T extends Array<any>> = T extends [infer A, ...infer B] ? B : [];
// 用于把指定类型 E 作为第一个元素添加到 T 数组类型中
type Unshift<T extends any[], E> = T extends [...args: infer U] ? [E, ...U] : never;
// 移除第一个类型
type Shift<T extends any[]> = T extends [infer F, ...infer Rest] ? Rest : T
// 用于把指定类型 E 作为第最后一个元素添加到 T 数组类型中
type Push<T extends any[], V> = T extends [...infer U] ? [...U, V] : never;
extends
泛型约束,可以做三目运算符做判断是否满足约束
type A<V, T> = {
[K in keyof V as V[K] extends T ? K : never]: V[K];
};
type B<T, K extends string> = {
[P in keyof T as T[P] extends K ? K : never]: T[P];
}
内置泛型类
以下内容都 只能处理 第一层属性, 不能深度影响
// 测试接口
interface UserInfo {
readonly id: string;
name: string;
age?: number
hobby?: {
football: boolean
basketball: boolean
}
}
Partial
Partial<T>
: 将类型的属性变成可选
type A = Partial<UserInfo>
// 深度可选
type DeepPartial<T> = {
[U in keyof T]?:
T[U] extends object ? // 判断是不是对象
DeepPartial<T[U]> : // 是则继续递归
T[U]
}
type B = DeepPartial<UserInfo>
Required
Required<T>
: 将类型的属性变成必选
type C = Required<UserInfo>
type DeepRequired<T> = {
// [U in keyof T]: T[U] extends object ? DeepRequired<T[U]> : T[U]
// -? 代表 移除 ? 变为必填 ---- 跟不写没什么区别
[U in keyof T]-?: T[U] extends object ? DeepRequired<T[U]> : T[U]
}
Readonly
Required<T>
: 将属性变为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type D = Readonly<UserInfo>
Pick
Pick<T>
: 从类型中 拿出 想要的属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
type PickAttr = Pick<UserInfo, 'id' | 'name'>;
const user1: PickAttr = {
id: "id",
name: 'name',
};
Record
Record<K extends keyof any, T>
: 将 K 中所有的属性的值转化为 T 类型。
type KeyType = number | string | symbol
type Record<K extends KeyType, T> = {
[P in K]: T
}
interface PageInfo {
title: string;
}
type Page = "home" | "about" | "contact";
const x: Record<Page, PageInfo> = {
about: { title: "about" },
contact: { title: "contact" },
home: { title: "home" },
};
ReturnType
ReturnType<T>
: 用来得到一个函数的返回值类型
const fnc = () => {
return 1
}
type MyFnc = typeof fnc
type Fnc = ReturnType<MyFnc>
const Pro = () => {
return Promise.resolve({
a: 1,
b: '2'
})
}
type c = Awaited<ReturnType<typeof Pro>>
Exclude
Exclude<T, U>
: 将某个类型中属于另一个的类型移除掉。
类似 Omit
, 但是只能对 联合类型
使用, 对象使用拿到的是 never
。
type Exclude<T, U> = T extends U ? never : T;
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
Extract
Extract<T, U>
: 从 T 中提取出 U。
类似 Pick
, 用法 同 Exclude
。
type Extract<T, U> = T extends U ? T : never;
type T3 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T4 = Extract<string | number | (() => void), Function>; // () =>void
type MyEvent = {
type: 'click',
event: MouseEvent
} | {
type: 'foucs',
event: FocusEvent
} | {
type: 'keydown',
event: KeyboardEvent
}
type REvent = Extract<MyEvent, { type: 'click' }>
type RcEvent = Exclude<MyEvent, { type: 'click' }>
Omit
Omit<T, K extends keyof any>
: 使用 T 类型中除了 K 类型的所有属性,来构造一个新的类型。
简单讲就是:剔除掉不要的类型。
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type OmitUser = Omit<UserInfo, 'age' | 'hobby'>
/**
* NonNullable<T> 的作用是用来过滤类型中的 null 及 undefined 类型
*
*/
type NonNullable<T> = T extends null | undefined ? never : T;
type T5 = NonNullable<string | number | undefined>; // string | number
type T6 = NonNullable<string[] | null | undefined>; // string[]
Parameters
Parameters<T>
: 用于获得函数的参数类型组成的元组类型。
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
type T7 = Parameters<(str: string, count: number) => void>; // [str: string, count: number]
type T8 = Parameters<typeof Array.isArray>; // [any]
type T9 = Parameters<typeof parseInt>; // [string, (number | undefined)?]
type T10 = Parameters<typeof Math.max>; // number[]
const obj = (c: {
a: {
b: 1
},
c: {}
}, d: 2) => {}
type Obj = Parameters<typeof obj>
type CA = Obj[1]
进阶
类
class Person {
public readonly id: string // 实例可用 - 只读
public name: string // 实例可用
private age?: number // 类本身使用, 对应一些get set方法给外部用
protected sex?: 'boy' | 'girl' // 类本身和子类使用
static hobby: Array<string> // 类使用
constructor(id: string, name: string) {
this.id = id
this.name = name
}
}
class ZhangSan extends Person {
constructor(id: string, name: string) {
super(id, name)
this.sex
}
}
const zs = new Person('1', '张三')
Person.hobby
抽象类
- 抽象类描述的是一种抽象的概念,无法被实例化,只能被继承。
- 抽象类继承在抽象类中,可以不实现,表示属于该抽象类的一个方法,如果继承的非抽象类中,那么抽象类的方法必须需要实现
抽象类对事物进行抽象,更多的是为了继承,为了扩展,为了实现代码的重用
abstract class Animal {
// 抽象方法,没有具体实现
abstract makeSound(): void;
// 具体方法
move(): void {
console.log('Animals can move.');
}
}
// 实现抽象类
class Dog extends Animal {
makeSound(): void {
console.log('Woof! Woof!');
}
}
// 实现抽象类
class Cat extends Animal {
makeSound(): void {
console.log('Meow!');
}
}
// 使用
const dog = new Dog();
dog.makeSound(); // 输出: Woof! Woof!
dog.move(); // 输出: Animals can move.
const cat = new Cat();
cat.makeSound(); // 输出: Meow!
cat.move(); // 输出: Animals can move.
abstract class Person {
name: string;
abstract speak(): void;
}
class Man extends Person {
// 如果不实现此方法,那么会报错:非抽象类“Man”不会实现继承自“Person”类的抽象成员“speak”
speak(): void {
console.log('说话');
}
}
abstract class Woman extends Person {
// 抽象类继承抽象类,可以不用具体实现,表述属于抽象类的一个方法
abstract speak(): void;
}
class Girl extends Woman {
// 如果不实现此方法,那么会报错:非抽象类“Man”不会实现继承自“Person”类的抽象成员“speak”
speak(): void {
console.log('唱歌');
}
}
// let p = new Person() // 报错:无法创建抽象类的实例
let m = new Man();
m.speak(); // 说话
let g = new Girl();
g.speak(); // 唱歌
接口实现类
使用 implements
给 class
类做约束 ,类型使用 ,
隔开。
interface AnimalIfc {
id: string
name: string
getName(): string
}
interface Age {
age: number
}
class Dog implements AnimalIfc, Age {
id: string
name: string
age: number
constructor(id: string, name: string, age: number) {
this.id = id
this.age = age
this.name = name
}
getName(): string {
return this.id
}
}
// 获取实例的类型
type ABC = InstanceType<typeof Dog>
泛型类
class BaseDefault<T> {
public val: T
constructor(val: T) {
this.val = val
}
getVal(): T {
return this.val
}
}
const type = new BaseDefault('2')
常用类型
指定属性可选
interface Article {
id: string
name: string
date: Date
desc?: string
}
// 基于 Article 的新类型,内容一致,必填属性不一样,重复书写会显得 冗余
/* interface CreateArticle<T> {
id?: T
name: string
date: Date
desc?: T
} */
// 将 指定 属性变为可选 - 1
type SetPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
// 指定可选 - 2
type Simplify<T> = {
[P in keyof T]: T[P]
}
type SetOptional<T, K extends keyof T> =
Simplify<Partial<Pick<T, K>> & Pick<T, Exclude<keyof T, K>>>
// 使用
type CreateArticle = SetPartial<Article, 'id' | 'desc'>
const article: createArticle = {
name: '1',
date: new Date()
}
// 指定必填
type SetRequired<T, K extends keyof T> = Simplify<Pick<T, Exclude<keyof T, K>> & Required<Pick<T, K>>>
其余写法
type SetOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type SetOptionalOmit<T, K extends keyof T> = Pick<T, K> & Partial<Omit<T, K>>;
type SetRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
type SetRequiredOmit<T, K extends keyof T> = Pick<T, K> & Required<Omit<T, K>>;
获取所有可选属性
// 拿到 所有的 可选属性
type GetPartial<T> = {
[K in keyof T as T[K] extends Required<T>[K] ? never : K]: T[K]
}
type OptionalKeys<T> = {
[P in keyof T]: Pick<T, P> extends Required<Pick<T, P>> ? never : P
}[keyof T]
const article2: GetPartial<CreateArticle<string>> = {
id: '1',
desc: '2'
}
空对象类型
type PropertyKey = string | number | symbol
type EmptyObject = {
[K in PropertyKey]: never
}
// 测试用例
const shouldPass: EmptyObject = {}; // 可以正常赋值
const shouldFail: EmptyObject = { // 将出现编译错误
prop: "TS"
}
type SomeType = {
prop: string
}
type Exclusive<T1, T2 extends T1> = {
[K in keyof T2]: K extends keyof T1 ? T2[K] : never
}
// 更改以下函数的类型定义,让它的参数只允许严格SomeType类型的值
function takeSomeTypeOnly<T extends SomeType>(x: Exclusive<SomeType, T>) { return x }
// 测试用例:
const x = { prop: 'a' };
takeSomeTypeOnly(x) // 可以正常调用
const y = { prop: 'a', addditionalProp: 'x' };
takeSomeTypeOnly(y) // 将出现编译错误
非空数组
type NonEmptyArray<T> = [T, ...T[]]
type NonEmptyArray<T> = T[] & { 0: T };
type NonEmptyArray = {
[P in (keyof T[] & 0)]: P extends number ? T | undefined : T[][P]
}
移除readonly
type Foo = {
readonly a: number;
readonly b: string;
readonly c: boolean;
};
type Mutable<T, Keys extends keyof T = keyof T> = {
-readonly [K in Keys]: T[K]
} & Omit<T, Keys>
const mutableFoo: Mutable<Foo, 'a'> = { a: 1, b: '2', c: true };
mutableFoo.a = 3; // OK
mutableFoo.b = '6'; // Cannot assign to 'b' because it is a read-only property.
mutableFoo.c = '6'; // Cannot assign to 'c' because it is a read-only property.
const mutableFoo2: Mutable<Foo, 'a' | 'b'> = { a: 1, b: '2', c: true };
mutableFoo2.a = 3; // OK
mutableFoo2.b = '6'; // OK
mutableFoo2.c = '6'; // Cannot assign to 'c' because it is a read-only property.
从类型中挑选出符合条件的
interface Example {
a: string;
e: number;
b: string | number;
c: () => void;
d: {};
f: string | number | boolean;
}
// 定义
type ConditionalPick<V, T> = {
[K in keyof V as V[K] extends T ? K : never]: V[K];
};
// 使用
type StringKeysOnly = ConditionalPick<Example, string | number>;
为函数新增参数
使用 Parameters 和 ReturnType 工具类型
type AppendArgument<F extends (...args: any) => any, A>
= (x: A, ...args: Parameters<F>) => ReturnType<F>
type Fn = (a: number, b: string) => number
type FinalF = AppendArgument<Fn, boolean>
// (x: boolean, a: number, b: string) => number
使用 infer 方式
type AppendArgument<F, T> = F extends (...args: infer Args) => infer Return ?
(x: T, ...args: Args) => Return : never
type Fn = (a: number, b: string) => number
type FinalFn = AppendArgument<Fn, boolean>
// (x: boolean, a: number, b: string) => number
函数参数属性一致
我们希望参数 a
和 b
的类型都是一致的,即 a
和 b
同时为 number
或 string
类型。当它们的类型不一致的值,TS 类型检查器能自动提示对应的错误信息
// 使用函数重载
function f(a: string, b: string): string
function f(a: number, b: number): number
function f(a: string | number, b: string | number ): string | number {
if (typeof a === 'string') {
return a + ':' + b;
} else {
return ((a as number) + (b as number));
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
// 把参数组合成一种类型//
const isStrArr = (a: string[] | number[]): a is string[] => typeof a[0] === 'string'
function f(...args: string[] | number[]) {
if (isStrArr(args)) {
return args[0] + ':' + args[1];
} else {
return args[0] + args[1];
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
比较类型相等
type IsEqual<T, U> =
(<G>() => G extends T ? 1 : 2) extends
(<G>() => G extends U ? 1 : 2)
? true
: false;
//test case, it is special
type X=IsEqual<{x:any}, {x:number}>; // false
type IsEqual<A, B> = A extends B ? (B extends A ? true : false) : false;
// 测试用例
type E0 = IsEqual<1, 2>; // false
type E1 = IsEqual<{ a: 1 }, { a: 1 }>; // true
type E2 = IsEqual<[1], []>; // false
类型包含关系
用于判断指定的类型 E
是否包含在 T
数组类型中
type Includes<T extends Array<any>, E> = T extends [infer A, ...infer B]
? IsEqual<A, E> extends true
? true
: Includes<B, E>
: false;
type I0 = Includes<[], 1>; // false
type I1 = Includes<[2, 2, 3, 1], 2>; // true
type I2 = Includes<[2, 3, 3, 1], 1>; // true
合并类型
实现一个 Merge
工具类型,用于把两个类型合并成一个新的类型。第二种类型(SecondType)的 Keys
将会覆盖第一种类型(FirstType)的 Keys
。具体的使用示例如下所示:
// 1
type Merge<FirstType, SecondType> = Omit<FirstType, keyof SecondType> & SecondType
// 2
type Merge <FirstType, SecondType> = {
[K in keyof (FirstType & SecondType)]:
K extends keyof SecondType ?
SecondType[K] :
K extends keyof FirstType ?
FirstType[K] :
never
}
// 3
type ExtractType<FirstType, SecondType> = {
[K in Extract<keyof FirstType, keyof SecondType>]: SecondType[K]
};
type Merge <FirstType, SecondType> = Omit<FirstType, keyof SecondType> & ExtractType<FirstType, SecondType>;