与Typescript

时间:2018-12-13 09:41:05

标签: typescript typescript-typings

我有一个通过映射对象类型TMap

将名称与类型相关联的类型

它旨在为处理程序函数提供一种命名类型与对应类型值的关联

interface Thing<TMap extends { [k: string]: any }> {
  get<T extends keyof TMap>(handler: (v: TMap[T], t: T) => unknown): unknown
}

例如,将“映射对象”类型设置为:

interface Types {
  num: number,
  dat: Date,
  str: string
}

和事物实例:

declare const thing: Thing<Types>

使用get方法获得几个值,类型可以工作,但在检查类型时会失去类型关联:

thing.get((v, t) => {
  // v: string | number | Date
  // t: "num" | "dat" | "str"
  if (t === 'num') {
    //  v: string | number | Date
    v
  } else if (t === 'dat') {
    //  v: string | number | Date
    v
  } else if (t === 'str') {
    //  v: string | number | Date
    v
  }
})

我设法解决了一个棘手的问题:

type Caster<TMap extends { [k: string]: any }> =
  <T extends keyof TMap>(
    v: TMap[keyof TMap],
    t: keyof TMap,
    isOfType: T
  ) => v is TMap[T]

declare const caster: Caster<Types>

thing.get((v, t) => {
  // v: string | number | Date
  // t: "num" | "dat" | "str"
  if (caster(v, t, 'num')) {
    //  v:  number 
    v
  } else if (caster(v, t, 'dat')) {
    //  v: Date
    v
  } else if (caster(v, t, 'str')) {
    //  v: string
    v
  }
})

如何正确声明ThingThing.get以保持类型关联以避免棘手的黑客攻击?

[编辑] 在TSPlayground

上查看

1 个答案:

答案 0 :(得分:2)

类型防护不支持将值的类型缩小到与选中的值不同的类型。

我们所能做的就是更改回调以接收一个对象,该对象是一个联合体,其中联合体的成员具有{ type: P, value T[P] }的形式,其中T是地图类型,P依次T

的每个属性
// Type that creates a union using the distributive property of union types.
type TypeUnion<T> = keyof T extends infer P ? // Introduce an extra type parameter P to distribute over
  P extends any ? { type: P, value: T[P] } :  // Take each P and create the union member
  never : never;

interface Thing<TMap extends { [k: string]: any }> {
  get(handler: (v: TypeUnion<TMap>) => unknown): unknown
}
declare const thing: Thing<Types>


interface Types {
  num: number,
  dat: Date,
  str: string
}

thing.get(o => { // o is  { type: "num"; value: number; } | { type: "dat"; value: Date; } | { type: "str"; value: string; }
  if (o.type === 'num') {
    o.value // number
  } else if (o.type === 'dat') {
    o.value // Date
  } else if (o.type === 'str') {
    o.value // string
  }
})

TypeUnion的条件类型的替代方法是使用映射类型,如下所示:

type TypeUnion<T> = {
  [P in keyof T] : { type: P, value: T[P]}
}[keyof T]