typeScript-基础篇(四)

交叉类型和联合类型

  • 交叉类型比较适合作对象的混入;
  • 联合类型可使类型具备不肯定性,能够加强代码的灵活性
// 交叉类型: 取全部类型的并集
interface DogInterface {
    run(): void
}
interface CatInterface {
    jump(): void
}

let pet:DogInterface & CatInterface = {
    run(): void
     jump(): void
}

联合类型数组

// 联合类型就是声明的类型并不肯定,能够为多个类型中的一个
 let a: string | number = "1"
 
// 字面量联合类型
// 有时候咱们不只要限制变量的类型,还须要限制变量的取值在某个特定的范围内
// 字符串联合类型
let c: 'a' | 'b' | 'c'
// 数字联合类型
let b: 1 | 2 | 3
// 对象联合类型

class Dog implements DogInterface{
     run(): void
     eat(): void
}

class Cat implements CatInterface{
     jump(): void
     eat(): void
}

enum Master {
    Boy,
    Girl
}
function getPet(master: Master){
    let pet = master === Master.Boy ? new Dog(): new Cat()
    // 若是一个对象是联合类型,在类型未肯定的状况下,只能访问全部类型的公有成员,这种状况下只能访问全部类型的交集
    pet.eat()
    return pet;
}
可区分的联合类型
  • 本质上是结合了联合类型和字面量类型的一种类型保护方法, 若是一个类型是多个类型的联合类型,而且每一个类型之间有个公共的属性,那么就能够凭借这个公共属性,来建立不一样的类型保护区块
interface Square {
    kind: "sauare",
    size: number
}
interface RectAngle {
    kind: "rectAngle",
    width: number,
    height: number
}
interface Circle {
    kind: "circle",
    r: number
}

type Shape = Square | RectAngle | Circle
function area(s: Shape) {
    switch (s.kind) {
        case: sauare: 
        return s.size * s.size
        
        case: rectAngle:
        return s.height * s.width
        
         case: circle:
        return Math.PI * s.r * s.r
        
    }
}
// 没有创建circle保护区块,不会报错
console.log(area({kind: 'circle', r: 2}))
// 解决此问题方法1:加返回值: number,
// 此时ts会判断,全部的switch分支是否是包含了全部的状况;
function area(s: Shape): number

// 解决此问题方法2:
 default:
    return ((e: never) => {throw newError(e)})(s)
// 此函数的做用就是:
// 检查s是否是never类型,若是是never类型,说明前面的全部分支都被覆盖了, 那么这个分支永远不会走到;
// 若是s不是never类型,就说明前面的分支有遗漏,须要补上那个分支

索引类型

  • 索引类型能够实现对对象属性的查询和访问,
  • 再配合泛型约束,就使咱们 可以创建对象,对象属性,以及属性值之间的约束关系
  • 索引类型的查询操做符
    • keyof T
    • 表示类型 T 的全部公共属性的字面量联合类型;
// key的类型是a和b的字面量联合操做类型
interface Obj {
    a: number;
    b: string;
}
let key: keyof Obj
  • 索引访问操做符
    • T[K]
    • 表示对象T的属性k所表明的类型
let value: Obj["a"]
  • 泛型约束
    • T extends U
    • 表示泛型变量能够经过继承某个类型或某些属性
let obj = {
    a: 1,
    b: 2,
    c: 3
}
function getValues(obj: any, keys: string[]){
    return keys.map(key => obj[key])
}

console.log(getValues(obj, [a, b])) // 1,2
console.log(getValues(obj, [e, f])) // [undefind, undefind] 
// 此时ts应该报错
// 通过改造后的函数

function getValues<T,K extends keyof T>(obj: T,keys: K[]): T[K][]{
    // 先定义个泛型变量T,用它来约束obj
    // 再定义一个泛型变量K,用它来约束keys数组
    // 把 K 增长一个类型约束,让他来继承obj全部属性的联合类型,K extends keyof T;
    // 函数的返回值是一个数组[],数组的类型就是属性K对应的类型T[K]
    // 这样就经过一个索引类型把getValues改造完毕了,这时ts类型检查就发挥做用了
    // 若是咱们指定一个再也不obj范围内的属性,ts就会报错
}

映射类型

  • 经过映射类型能够从一个旧的类型生成一个新的类型;
  • 本质上是一种预先定义的泛型接口,一般还会结合索引类型, 获取对象的属性和属性值,从而将一个对象映射成咱们想要的结构
interface Obj {
    a: string;
    b: number;
    c: boolean;
}

// 好比把接口中的全部属性变为只读;
type ReadOnlyObj = Readonly<Obj>
// 把接口中的全部属性变成可选
type PartialObj = Partial<Obj>
// 抽取接口中的子集
type PickObj = Pick<Obj,'a' | 'b'>
// 同态
// 意思是只会做用于Object属性而不会引入新的属性;

// 非同态类型
type RecordObj = Record<'x' | 'y', Obj>




// 定义一个类型别名ReadOnlyObj
// 接口名称就是Readonl y
// 接口中传入的类型就是Obj

// ts内置类库Readonly
type Readonly<T> = {
    readonly [P in keyof T]: T[P]
}

1. Readonly是一个泛型接口,并且是一个可索引类型的泛型接口
2. 索引签名是 P in keyof T
3. in keyof T 是一个 索引类型的查询操做符,表示T全部属性的联合类型
4. p in 这里执行了一次for in 操做;它会把变量P依次绑定到T的全部的属性上
5. 索引签名的返回值就是索引操做符了T[P],表明属性P 所指定的类型了;
6. 最后加上readonly,就把全部的属性变成了只读;

条件类型

T extends U ? X : Y
// 若是类型T,能够被赋值给类型U,结果类型就是X类型,不然Y类型;
// 条件类型使类型具备了不惟一性,同时也增长了语言的灵活性;

type TypeName<T> =
    T extends string ? 'string' :
     T extends number ? 'number' :
      T extends boolean ? 'boolean' :
       T extends undefined ? 'undefined' :
        T extends Function ? 'Function' :
        "object";

type T1 = TypeName<string>  // string
type T2 = typeName<string[]> // object

分布式条件类型

(A | B) extends U ? X : Y
// (A extends U ? X : Y) | (B extends U ? X : Y)
// 若是T 是一个联合类型的状况下,这时候的结果类型就会变成多个条件类型的联合类型;

type T3 = TypeName<string | string[]>
// "string" | "object"
// T3就会被推断为string和object的字面量联合类型

// 利用这个特性能够帮咱们实现一些类型的过滤;
type Diff<T, U> = T extends U ? never : T
type T4 = Diff<"a" | "b" | "c", "a" | "e">
// type T4 = "b" | "c"
// Diff会被拆解为多个条件类型的联合类型:
// Diff<"a", "a" | "e"> | Diff<"b", "a" | "e"> | Diff<"c", "a" | "e">
// never | "b" | "c"
// "b" | "c"
// 做用:能够过滤掉类型T中能够赋值给类型U的类型
// 能够基于类型 Diff中再作扩展,从类型中除去咱们不须要的类型,好比:null 和 undefind
type NotNull<T> = Diff<T, undefind | null >
type T5 = NotNull<string | number | undefind | null>
type T5 = string | number
  • 刚才实现的两个类型Diff 和 NotNull,官方已经为咱们实现了,分别是
  • Exclude<T, U>
    • 从类型T中过滤掉能够赋值给U的类型
  • NonNullable<T>
  • Extract<T, U>
    • 从类型T中抽取能够赋值给U的类型,与Exclude恰好相反
type T6 = Extract<"a", "b", "c", "a" | "e"> // a
  • ReturnType<T>
    • 能够获取一个函数返回值的类型 type T7 = ReturnType<() => string> // T7 -> string
type RetrunType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
// 1. RetrunType 要求参数 T 能够被赋值给一个函数
// 2. 这个函数的参数和返回值都是any;
// 3. 因为这里函数的返回值是不肯定的,用了一个infer关键字
// 4. infer的关键字的做用是待推断或者延迟推断,须要根据实际状况来肯定
// 5. 若是实际状况是R,则返回R,不然返回值类型就是any;
相关文章
相关标签/搜索