使用鉴别器编写泛型类型的Typescript-运行时访问鉴别器



我认为这不可能,但我想问一下。我有带鉴别器的类型,例如:

type Fish={
type: "fish"
canSwim: boolean
}
type Bird={
type: "bird"
canFly: boolean
}

我有一个状态对象,例如:

const state={
fish: /* a fish */
bird: /* a bird */
}

我想写一个函数:

function getFromState<T>():T {
return state[T.type] as T
}

显然,解决方法是将类型作为参数传递,例如:

function getFromState<T>(type):T {
return state[type] as T
}

但你最终会重复一些事情:

const animal=getFromState<Fish>("fish")

我想您已经知道,由于类型擦除,您不能在运行时引用类型。

有许多替代路径,但这完全取决于您的实际用例。根据你写的问题很难知道你的真正需求是什么。我建议你用你的实际用例重写你的问题,而不是抽象的Animal。我会在这个暂定答案中猜测你的需求,但如果你更新你的问题并在上面@我或在这个答案下面评论,我会更新它。

在运行时以下的代码中;多态性";使用示例中的鉴别器属性(type(和常规if-elseswitch逻辑来实现。因为Typescript能够通过推理缩小类型,所以您还可以获得编译时静态类型安全性。在您的变通方法中没有DRY违规。

您可以在游乐场中测试其静态类型检查和运行时行为。

type Fish = {
type: "fish"
canSwim: boolean
}
type Bird = {
type: "bird"
canFly: boolean
}
type Animal = Fish | Bird
type AnimalTypes = Animal['type']
function getAnimal(type: AnimalTypes): Animal {
return type === 'fish' ? { type: 'fish', canSwim: true } : { type: 'bird', canFly: true }
}
const state = {
fish: { type: 'fish', canSwim: true } as Fish,
bird: { type: 'bird', canFly: false } as Bird
}
function getFromState(type: AnimalTypes):Animal {
return state[type]
}
function polymorphicFunction(animal: Animal) {
if (animal.type === 'fish') {
console.log(`It ${animal.canSwim ? 'can' : 'cannot'} swim!`)
} else {
console.log(`It ${animal.canFly ? 'can' : 'cannot'} fly!`)
}
}
// both static type checking and runtime "polymorphism" works.
// Nothing has been repeated (DRY).
polymorphicFunction(getFromState('fish'))
polymorphicFunction(getFromState('bird'))

按操作编辑:在@Inigo的帮助下,并回答了另一个问题(基于鉴别器的Typescript窄参数类型(,使用实用程序类型Extract:找到了更完整的解决方案

type Fish = {
type: "fish"
canSwim: boolean
}
type Bird = {
type: "bird"
canFly: boolean
}
type Animal = Fish | Bird
const state = {
fish: [{ type: 'fish', canSwim: true }, { type: 'fish', canSwim: false }] as Fish[],
bird: [{ type: 'bird', canFly: true }, { type: 'bird', canFly: false }] as Bird[]
}
function getFromState<C extends Animal["type"]>(type: C): Extract<Animal, {type: C}> {
return state[type][0] as Extract<Animal, {type: C}>
}
getFromState("fish").canSwim // works
getFromState("bird").canFly // also works
getFromState("fish").canFly // error

游乐场链接

这里提供的答案给了我一些灵感,我想出了:

type Fish = {
type: "fish"
canSwim: boolean
}
type Bird = {
type: "bird"
canFly: boolean
}
type Animal = Fish | Bird
const state = {
fish: [{ type: 'fish', canSwim: true }, { type: 'fish', canSwim: false }] as Fish[],
bird: [{ type: 'bird', canFly: true }, { type: 'bird', canFly: false }] as Bird[]
}
function getFromState<C extends Animal["type"]>(type: C): Extract<Animal, {type: C}> {
return state[type][0] as Extract<Animal, {type: C}>
}
getFromState("fish").canSwim // works
getFromState("bird").canFly // also works
getFromState("fish").canFly // error

游乐场链接

最新更新