联合类型、交叉类型
- 交叉类型的问题:两个类型中有相同的属性且类型不同时,&集后的结果是never
// 两个类型中有相同的属性且类型不同时,&集后的结果是never
interface Person1 {
handsome:string,
gender:number
meta:{
n:number
}
}
interface Person2 {
high:string,
gender:string
meta:{
n:string
}
}
type Person3 = Person1 & Person2
type IGender = Person3['meta']['n'];
- 交叉类型可以快速扩展属性
let obj = {name:'jw',age:30}
let person:{name:string,age:number,address:string} = obj as typeof obj & {address:string}
extends 条件类型
子类型 extends 父类型 = true
- never是任何类型的子类型 最small 嘻嘻
- never类型 - 字面量类型 -基本类型 -包装类型 <-any unknow <- obejct/Object/{}
type T1 = never extends "str" ? true : false;// true
type T2 = "str" extends string ? true : false;// true
type T3 = string extends String ? true : false;// true
// 万物皆对象
type T4 = String extends object ? true : false;
type T5 = string extends {} ? true : false; // true
type T6 = string extends object ? true : false; // false
// 基本类型 extends 对象类型 ???去你的吧 又看类型又看结构,类型不对直接false 马德
//再看看这个
// objext {} Obejct 这个三个怎么相互extends都是true 马德一伙儿得
- obejct/Object/{}的区别
- obejct 只针对对象类型
String、Number、Function {} extents obejct | object extends {}{}和obejct可以看成字面量类型/结构 Object extends object从结构看的,所以返回trueObject extends {}从结构看的,所以返回true
- obejct 只针对对象类型
分发机制(条件类型)
- any自带分发机制
- 条件类型是有分发机制的 1 + 除了1的部分 第一次用1和1比返回true(如果不是true而且其他值则返回对应的值)
- 第二次用非1的值和1去比返回false
- 两次结果作为联合类型返回
true|false = boolean类型
type PaoPao = any extends 1 ? true : false // boolean ???凭什么
- never通过泛型传入,只返回never
type t<T> = T extends 1 ? true : false
type t2 = t<never> // t2:never 气昏了 吗德
- 通过条件类型 来进行类型的区分,条件语句也可以实现约束的效果
- 分发触发的机制:
1.联合类型通过泛型传递 type xxx = res<Bird | Fish>2.比较的时候(extends)产生的分发 type res<T extends Bird | Fish>3.类型需要是裸类型 泛型
裸类型又是什么???
- 裸类型就是泛型T,一个人用不跟别人搭配。
那又如何搭配呢?? 教教我 - 方式一:
T & {} extends Bird ? Sky : Water跟{}交个朋友 经典解决分发 - 方式二:
T[] extends Bird[] ? Sky : Water
type t3 = 100 extends 100 | 200 ? true : false
interface Bird {
name: '鸟'
}
interface Water {
name: '水'
}
interface Fish {
name: '鱼'
}
interface Sky {
name: '天空'
}
// type res<T extends Bird | Fish> = T extends Bird ? Sky : Water 会产生分发,如何解决呢?如下: T & {}
type res<T extends Bird | Fish> = T & {} extends Bird ? Sky : Water
type t4 = res<Bird | Fish>
- 分发的场景
type NoDistribute<T> = T & {};
// 分发机制有的场景需要,有的场景需要禁用, 不能一概而论。
type UnionAssets<T, K> = NoDistribute<T> extends K ? true : false;
type U1 = UnionAssets<1 | 2, 1 | 2 | 3>; // true
/*
1 比 1|2 // true
2 比 1|2// true
3 比 1|2// false
true | false 联合一下 = boolean
*/
type U2 = UnionAssets<1 | 2 | 3, 1 | 2>; // boolean
// 判断两个类型是否完全一致 ? 1|2 1|2
type isEqual<T, K, S, F> = NoDistribute<T> extends K
? NoDistribute<K> extends T
? S
: F
: F;
type A2 = isEqual<1 | 2, 1 | 2, true, false>;
/*
1 extends 1|2 ? 1|2 extends 1 ? true : false : false 分发后值就被确定了拿去对比就出问题啦。。。
*/
type FormatVal<T> = T extends string
? string
: T extends number
? number
: never;
// 映射关系 可以考虑用泛型,参数个数不一致,类型和入参数无法,考虑重载
function sum<T extends string | number>(a: T, b: T): FormatVal<T> {
return a + (b as any)
}
let res = sum(1, 2)
内置类型
内置类型中有很多类型是基于条件类型的
- Extract 左边 包含 右边的值有哪些
- Exclude 左边 不包含 右边的值有哪些
- NonNullable 不为空的类型返回,否则返回never
-
T & {} 因为空对象不包含null和undefined,所以就被干掉了。
type Extract<T, U> = T extends U ? T : never; // 实现原理
type ExtractRes = Extract<1 | 2 | 3 | 4, 1 | 2 | 3| 5>; // 1|2|3
type Exclude<T, U> = T extends U ? never : T; // 实现原理
type ExcludeRes = Exclude<1 | 2 | 3 | 4, 1 | 2>;
const ele = document.getElementById("app");
// type NonNullable<T> = T extends null | undefined ? never : T;
// type NonNullable<T> = T & {}; // 实现原理
type Ele = NonNullable<typeof ele>;
除了基于条件类型之外,我门还有基于对象类型
- Compute 合并两个interface
interface Person {
name: string;
age: number;
}
interface Person2 {
name2: string;
age2: number;
sex: string;
}
type Compute<T> = {
[key in keyof T]?: T[key] // 加了? = partial的实现 全部可选
}
type p = Compute<Person & Person2>
- Partial 设置全部key为可选的
深度可选的自己实现 type r5 = DeepPartial<Compny> - Required全部必填 -?
深度必填的自己实现 - Readonly全部只读
深度必填的自己实现
type DeepPartial<T> = {
[key in keyof T]?: T[key] extends object ? DeepPartial<T[key]> : T[key]
}
interface Person {
name: string;
age: number;
}
interface Compny {
name: string;
address: string;
person: Person;
}
type r5 = DeepPartial<Compny>
const test: r5 = {
name: 'paopao',
person: {}
}
type Required<T> = {
[key in keyof T]-?: T[key] extends object ? Required<T[key]> : T[key]
}
type Readonly<T> = {
+readonly [K in keyof T]: T[K];
};
type ReadonlyRes = Readonly<IPerson>;
// 不能修改就是readonly , 却掉readonly
type Mutate<T> = {
-readonly [K in keyof T]: T[K];
};
type MutateRes = Mutate<ReadonlyRes>;
- Omit 挑选出需要的属性返回
- Pick 删除不需要属性,保留剩下的返回
type Pick<T, K extends keyof T> = {
[key in K]: T[key];
};
type PickRes = Pick<IPerson, "company" | "age">;
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type OmitRes = Omit<IPerson, "name" | "age">;
类型推断 infer、inference
- infer关键字只能在条件类型中使用,用来提取类型某一个部分的类型
比如函数的参数/返回值 ReturnType获取返回值类型Parameters获取参数类型InstanceType获取类的实例类型ConstructorParameters获取构造函数的参数类型
// ReturnType
type ReturnType<T> = T extends (...args: any[])=> infer R ? R : never
type r = ReturnType<typeof foo>
type Parameters<T> = T extends (...args: infer R)=> any ? R : never
type p = Parameters<typeof foo>
class Person {
constructor(a: string, b: string) {}
}
// type ConstructorParameters<T extends { new (...args: any[]): any }> =
// T extends { new (...args: infer P): any } ? P : any;
type ClassConstructorParameters = ConstructorParameters<typeof Person>;
// type InstanceType<T extends { new (...args: any[]): any }> = T extends {
// new (...args: any[]): infer R;
// }
// ? R
// : any;
type ClassInstanceType = InstanceType<typeof Person>;
// 如何讲一个元组转化成联合和类型
type ITumple = [string, number, boolean]; // string | number | boolean
// type TumpleToUniom = ITumple[number];
type ElementOf<T extends any[]> = T extends Array<infer R> ? R : never;
type TumpleToUniom = ElementOf<ITumple>;
// 对元组进行参数移动, 将元组头尾进行交换 . infer 要配合 extends
type SwapHeadTail<T extends any[]> = T extends [
infer Head,
...infer Body,
infer Tail
]
? [Tail, ...Body, Head]
: never;
type Res = SwapHeadTail<["jw", 1, 2, 3, 4, 5, 30]>;
Promise的递归
// infer 可以递归推断
type PromiseVal<T> = T extends Promise<infer R> ? PromiseVal<R> : T;
type PromiseReturnVal = PromiseVal<Promise<Promise<200>>>;
Record描述对象类型 取代object
// 推导出类型 (根据泛型的位置 来推导具体的类型)
function map<T extends keyof any, K, R>(
obj: Record<T, K>,
callback: (value: K, key: T) => R
) {
let result = {} as Record<T, R>;
for (let key in obj) {
result[key] = callback(obj[key], key);
}
return result;
}
map({ name: "jw", age: 30 }, (value, key) => {
return true;
});
类型声明
ts兼容性分成两种 子 extends 父 结构来考虑,以下是第一种
- 字符串从结构类型
let str: string = "abc"; // 类型层级
let obj!: { toString(): string };
obj = str; // 结构来考虑 extends object extends {}
- 函数兼容性 (参数和返回值的兼容性)
- 对于函数而言他的兼容性, 少的可以赋予给多的, 参数少的是子类型
- 返回值要求安全, 返回值要求是子类型.
let sum1 = (a: number, b: number): string | number => a + b;
let sum2 = (a: number): number => a;
type Sum1 = typeof sum1;
type Sum2 = typeof sum2;
type X = Sum2 extends Sum1 ? true : false;
const forEach = <T>(
arr: T[],
callback: (val: T, key: number) => string | number
) => {
for (let i = 0; i < arr.length; i++) {
let r = callback(arr[i], i); // 调用函数的时候 会传递多个参数
}
};
forEach(["A", 2, 3, {}], function (val) {
return "abc";
});
- 类的兼容性 也是一样 比较的是实例
- 如果类中的属性 有private 或者protected则两个值不能互相复制
class A {
private a = 1;
}
class B {
private a = 1;
}
const b: B = new A();
ts中从标称类型来看
// 希望给基本类型做区分,达到差异化的目的
type withType<T, K> = T & [K];
type BTC = withType<number, "BTC">;
type USDT = withType<number, "USDT">; // 基于内置类型来做构建
const c1 = 100 as BTC;
const c2 = 100 as USDT;
function money(val: BTC) {}
money(c1);
逆变协变
- 逆变:在函数中可以标记儿子,但是传父亲
- 内部调用函数的时候 可以传递 Child 和 Grandson. 但是在使用属性时 只能认为最多就是child
- 协变:在函数中可以标记父亲,返回的是儿子
- 函数的返回值, 需要返回子类,因为内部代码在访问属性的时候要保证可以访问到
class Parent {
car() {}
}
class Child extends Parent {
house() {}
}
class Grandson extends Child {
sleep() {}
}
// 安全性考虑
// 1) 内部调用函数的时候 可以传递 Child 和 Grandson. 但是在使用属性时 只能认为最多就是child
// 2) 函数的返回值, 需要返回子类,因为内部代码在访问属性的时候要保证可以访问到
function fn(callback: (ctr: Child) => Child) {
// 我交给回调的有房、有车
let r = callback(new Child());
r.house;
}
fn((child: Parent): Grandson => {
return new Grandson();
});
type Arg<T> = (arg: T) => void;
type Return<T> = (arg: any) => T;
type ArgReturn = Arg<Parent> extends Arg<Child> ? true : false; // 基于函数参数的逆变
type ReturnReturn = Return<Grandson> extends Return<Child> ? true : false; // 返回值是协变的
// 逆变带来的问题(我们写业务的时候 还是要正常开启逆变)
interface MyArray<T> {
// concat: (...args: T[]) => T[];
concat(...args: T[]): T[]; // 这种写法不进行逆变检测,所有在描述对象中的方法时全部采用这种方式
}
// parent: (...args: Parent[]) => Parent[];
// child: (...args: Child[]) => Child[];
// 将child 赋予给parent 传父返儿子
let parentArr!: MyArray<Parent>;
let childArr!: MyArray<Child>;
// chilldArr 能不能赋予给 parentArr
// [{car(){}}] = [{car(){},house(){}}]
parentArr = childArr;
类型兼容性
- 两个枚举之间 不能兼容
enum E1 {
a = 1,
}
enum E2 {
a = 1,
}
// e2 = e1; //两个枚举之间 不能兼容
- 泛型兼容性, 如果生成的结果一致 类型就就兼容
type II<T> = { name?: T };
type X1 = II<string> extends II<string> ? true : false; // 生成结构一致即可
- 对象的兼容性, 多的属性可以赋予给少的
- 类型层级兼容性,never -> 字面量 -> 基础类型 -> 包装类型 -> any / unknown
- 子 extends 父 满足 即可赋值
类型推导的概念
- 赋值推断, 根据赋予的值来推断类型
- 函数时通过左边来推导右边, 基于上下文类型来进行自动的推导
- 函数返回值编辑成void ,赋予一个函数的时候,意味着不关心返回值
let name = "jw";
let age = 30; // 1.
// 2.
const sum: (a: string) => void = (a) => {
return a;
};
let r = sum("1");
function forEach1(arr: number[], callback: () => void) {
// 这里的void 不关心返回值 3.
callback();
}
forEach1([1, 2, 3], function () {
return [];
});
类型保护
- 基于js + ts (收窄)
- ts很多情况下 需要使用联合类型, 默认情况下只能使用公共的方法,识别类型 (针对某个类型进行处理)
- typeof(基础类型) instanceof(类类型) in(接口类型,可辨识类型)
function fn2(a: string | number) {
if (typeof a === "string") {
a; // string
} else {
a; // number
}
}
class Cat {
cry() {}
}
class Dog {
eat() {}
}
function getInstance(clazz: { new (...args: any[]): Cat | Dog }) {
return new clazz();
}
const instance = getInstance(Cat);
if (instance instanceof Cat) {
instance.cry;
} else {
instance.eat;
}
interface Bird {
kind: "鸟";
fly: string;
}
interface Fish {
kind: "鱼";
swim: string;
}
// 可辨识类型 通过in来实现
function getAimal(val: Bird | Fish) {
// 基于差异化来辨别
if ("fly" in val) {
val;
} else {
val;
}
if (val.kind == "鸟") {
val.fly;
} else {
val.swim;
}
- 函数的嵌套不识别的问题 ? ! if 都有缩小范围的用途 (基于上下文类型的推导,会因为作用域的变化 而产生问题)
function addType(val?: number) {
// 断言就是自己说的算,出错了自己承担
val = val || 0;
return function (type: string) {
return type + (val as number).toFixed(); // ts 无法识别的时候 需要用断言
};
}
addType(100)("$");
- is 语法 ts语法 主要在辅助的方法中用的比较多
// is 语法 ts语法 主要在辅助的方法中用的比较多
interface Bird {
kind: "鸟";
fly: string;
}
interface Fish {
kind: "鱼";
swim: string;
}
function isBird(val: Bird | Fish): val is Bird {
// ts的返回值类型
// 函数的名字 和返回值是无关的
// true是bird 还是false 是bird
return "fly" in val;
}
function getAimal(val: Bird | Fish) {
// 基于差异化来辨别
if (isBird(val)) {
val;
} else {
val;
}
}
unknown冷知识
- unknown 和 any 都是顶级的类型
- unknown 是无法识别的类型 没有key
- any不校验 意味着可以调用 可以取值,unknown 是any的安全类型
- 如果标识为unknown 类型 必须先类型保护再去使用 (收窄类型 在使用)
type keys1 = keyof any;
type keys2 = keyof unknown; //unknown 是无法识别的类型 没有key
type unionUnknown = unknown | string | true | false; // unknown 任何类型都可以赋予给unknown
type interUnknown = unknown & string; // string
function isNumber(val: unknown): val is number {
return typeof val === "number";
}
function isString(val: unknown): val is string {
return typeof val === "string";
}
if (isNumber(a)) {
a.toFixed; //
} else if (isString(a)) {
a.charCodeAt;
}