typescript入门:高级类型

学习typescript中 ,有一个小伙伴提出了一个问题typescript

const a = {
	a:'1',
	b:'2',
	c:'3'
}
复制代码

如何取到每一个键上的值 ,组成一个联合类型 ? 即获得一个类型为编程

type forA = "1" | "2" | "3"
复制代码

一位大神给出了答案数组

const a = {
	a:'1',
	b:'2',
	c:'3'
} as const
type t<T> = T[keyof T]
type forA = t<typeof a>  //"1" | "2" | "3"
复制代码

我已经开始迷迷糊糊了。接着提问者又延伸了这个问题markdown

const a = {
	a:'1',
	b:'2',
	c:'3',
	d:{
		f:'4'
	}
}
复制代码

如何取到每一个键上的值 (包括一个深层嵌套),组成一个联合类型 ? 即获得一个类型为ide

type forA = "1" | "2" | "3" | "4"
复制代码

过了一会,大神又拿出了回答函数

const a = {
	a:'1',
	b:'2',
	c:'3',
	d:{
		f:'4'
	}
} as const
type t<T> = {
    [K in keyof T]:T extends object 
    ? t<T[K]>
    : T[K]
}[keyof T]
type forA = t<typeof a> //"1" | "2" | "3" | "4"
复制代码

什么?类型里面还有递归!对知识查漏补缺,列下如下几点oop

  • 定义变量时的 as const
  • type 内的[K in keyof T]
  • extends A ? B : C 类型的三目表达式?
  • 类型递归?
  • 最后的 [keyof T]

keyof 和 typeof

  • keyof 得到接口或者type alias 的键学习

    interface interfaceNumber{
        one:string,
        two:number
    }
    type typeNumber  = {
        three:string,
        four:number
    }
    type interfaceKey = keyof interfaceNumber // "one" | "four" 
    type numberKey = keyof typeNumber // "three" | "four"
    复制代码
  • typeof使一个上文的某个 Javascript变量 转换为typescript中的类型ui

    const someType = {
        obj:{
            one:1
        },
        arr:[1,2,4],
        num:1
    }
    type t = typeof someType
    //{
    // obj: {
    // one: number;
    // };
    // arr: number[];
    // num: number;
    //}
    复制代码

    类型系统递归地把一个对象上键的值转换为对应类型spa

as const

把一个数字类型或者字符串类型在转换为类型时,缩紧为字面量。

const a = 1 as const;
type a1 = typeof a;// 1

const b = "i am b" as const;
type b1 = typeof b;// "i am b"

const c = [1,"23434"  , 1] as const
type c1 = typeof noArray // readonly [1, "23434", 1]
复制代码

如何理解字面量和string类型?字面量也是单独的一个类型,而类型string ,能够理解为无穷(全部)字面量的联合 "a" | "b" | "c"|....

它是字面量的全集。同理number

as const 还能够灵活穿插在对象中使用 , 可是数组不行。

const someType = {
    obj:{
        one:1 as const
    },
    num:1
}
//typeof someType
//{
// obj: {
// one: 1;
// };
// num: number;
//}
const Array = [1,2 as const , 1]
//typeof Array
//number[]
复制代码

索引类型 type[key]

索引到interface或者tpye alias 对应键的类型

const someType = {
    obj:{
        one:1
    },
    arr:[1,2,4],
    num:1
}
type t = typeof someType
type obj = t["obj"]//{ one:number }


interface T{
    a:string,
    t:{
        p:number
    }
}
type a = T["a"]//string
type p = T["t"]["p"]//number
复制代码

使用 t.obj 来索引类型是无效的 ,命名空间才能够这么作。

若是索引联合字符串,则结果也是被索引的键的类型的联合。

interface T{
    a:string,
    b:number
}
type t1 = T["a" | "b"]// string | number
//或者
type t2 = T[ keyof T ]// string | number
复制代码

映射类型[ K in keyof T]

只能在type中用。

type getType1<T>= {
    [K in keyof T]: T[K]
}
//报错
//计算属性名的类型必须为 "string"、"number"、"symbol" 或 "any"。ts(2464)
//成员“[K in keyof”隐式包含类型“any”。ts(7008)
interface getType2<T>{
    [K in keyof T]: T[K]
}
复制代码

可做为固定的语法 [K in keyof T] ,K表示全部 T 中的键被拆分红一个个键,后面能够声明类型

const someType = {
    obj:{
        one:1
    },
    num:2
}
type getType1<T>= {
    [K in keyof T]: T[K]
}
type instance1 = getType1<typeof someType>
//{
// obj:{
// one:string 
// }
// num:number
//} 
复制代码

keyof T 结果是一组key的联合,下面原始的语法也是可行的

type Keys = "option1" | "option2";
type Flags = { [K in Keys]: boolean };
复制代码

你已经发现。映射类型如同JavaScript中的 for(let K in T) 。如下是官方说明

它的语法与索引签名的语法类型,内部使用了 for .. in。 具备三个部分:

  1. 类型变量 K,它会依次绑定到每一个属性。
  2. 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
  3. 属性的结果类型。

咱们能够对 T[K] 作一些处理。

const someType = {
    obj:{
        one:1
    },
    num:2
}
//extends 相似于三目运算符
type getType1<T>= {
    [K in keyof T]: T[K] extends object ? 1 : 0 
}
//object类型转换为字面量1,其余类型为0
type instance1 = getType1<typeof someType>//{obj: 1;num: 0;}
复制代码

extends后面会说起,此处能够简单做为三目表达式。

泛型函数与递归

type或者interface,能够看做一个函数,它能够在{}内部调用自身。

const a {
	a:'1',
	b:'2',
	c:{
        d:5
    }
} as const
type t<T> = { [ K in typeof T]:T[K] extends object ? t<T[K]> : T[K] }
type forA = t<type of a>  //"1" | "2" | 5
复制代码

若是没有泛型参数,它也是泛型函数,也能够调用自身

//这并不报错
interface Map {
    [key: string]: Map;
}
复制代码

若是没有{},则不能够调用自身

//Add是把const类型相加的泛型函数
//报错:类型别名“F”循环引用自身。ts(2456)
type F<T extends number> = T extends 10 ? F<Operate.Add<T,1>> : 0 
复制代码

conditional Type:T extends U ? X : Y

extends 关键字

首先说明,typescript多处使用 extends 关键字,但它们的做用不尽相同。

//js中的类继承,与ts无关
class A extends B{}

//接口继承
interface Shape {
    color: string;
}
interface Square extends Shape {
    sideLength: number;
}
//泛型约束
//泛型参数T必须有用length键且类型为number
type getType1<T extends {length:number}>= {
    [K in keyof T]: T[K]
}
//约束K必须包括一个或以上T的键值
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
}
//conditional type
type F<T> = T extends object ? 0 : 1
复制代码

typescript中的extends三种场景,

  • 泛型约束,在你输入被约束的泛型参数时,强迫输入值与extends的运算为true

  • conditional type ,它的运算可真可假 ,true的结果返回X,false返回Y

  • 接口继承,若是interface A extends B{} , 那么A extends B 是必定为真的

以个人理解,它们含义上是一脉继承的。

extends运算

上面说extends的含义上有关联,可是实际conditional type 和 泛型约束在边缘状况下存在差异。

泛型约束

泛型约束的extends 结果要么是true 要么是 false,这个是明确的,它决定编译器的报错与否。

假设 type T<A extends B> ,如A或B有一边是联合类型时,它遵循着一个规则,A必须是B子集

具体而言就是,A的每一个元素extends B都要为真。

type B = "1" | "2"
type T<A extends B>= {
    [key in A] :0
}
T<"1"> // { "1":0 }
T<"1" | "2"> //{"1":0,"2":0}
T<"1" | "2" | "3">//报错 不能将类型“"3"”分配给类型“"2" | "1"”。ts(2344)
//至关于
//("1" extends B)&("2" extends B)&("3" extends B)
复制代码

conditional type

distributive conditional types

conditional types中,若是T是 naked type那么 conditional types就被称为分配条件类型(distributive conditional types)。

naked type,然而Typescript并无说明啥是naked type, 咱们大体能够认为就是这个type没有被包裹在其余的复合结构里,如 array , record , function等。如咱们能够经过将T包裹为[T]来破坏naked type

type F<T> = T extends U ? X : Y
type union_type = A | B | C
type a = F<union_type>
//那么a的结果为 A extends U ? X :Y | B extends U ? X :Y | C extends U ? X : Y
复制代码

有一点相似泛型约束的分配运算

其余更加特殊的状况,能够看这篇文章:深刻typescript类型系统(二): 泛型和类型元编程

最后 咱们再看看文章开头的答案

type t<T> = {
    [K in keyof T]:T extends object 
    ? t<T[K]>
    : T[K]
}[keyof T]
复制代码

已经能够分析清楚了~

刚刚入门typescript,只是凭借实践实例得出本身的理解,有误望指出。

相关文章
相关标签/搜索