带有鉴别器的 Typescript 通用类型 - 运行时访问鉴别器
Typescript generic type with discriminator - runtime access to discriminator
我认为这不可能,但我想问一下。我有带有鉴别器的类型,例如:
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")
正如我想您已经知道的那样,您不能在运行时引用类型,因为 type erasure。
有很多替代路径,但这完全取决于您的实际用例。根据您所写的问题,很难知道您的真正需求是什么。我建议你用你的实际用例(状态)而不是抽象的动物)重写你的问题。我会在这个暂定答案中猜测你的需求,但如果你更新你的问题并在上面@我或在这个答案下面发表评论,我会更新它。
在下面的代码中,运行时“多态性”是通过您的示例 (type
) 中的鉴别器 属性 和常规 if
-else
或 switch
逻辑。因为 Typescript 能够通过推理 narrow types ,所以您还可以获得编译时静态类型安全性。在您的解决方法中没有 DRY 违规。
你可以test both its static type checking and its runtime behavior in the Playground.
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'))
OP 编辑:
在@Inigo 的帮助和对另一个问题 () 的回答下,使用实用程序类型 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
我认为这不可能,但我想问一下。我有带有鉴别器的类型,例如:
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")
正如我想您已经知道的那样,您不能在运行时引用类型,因为 type erasure。
有很多替代路径,但这完全取决于您的实际用例。根据您所写的问题,很难知道您的真正需求是什么。我建议你用你的实际用例(状态)而不是抽象的动物)重写你的问题。我会在这个暂定答案中猜测你的需求,但如果你更新你的问题并在上面@我或在这个答案下面发表评论,我会更新它。
在下面的代码中,运行时“多态性”是通过您的示例 (type
) 中的鉴别器 属性 和常规 if
-else
或 switch
逻辑。因为 Typescript 能够通过推理 narrow types ,所以您还可以获得编译时静态类型安全性。在您的解决方法中没有 DRY 违规。
你可以test both its static type checking and its runtime behavior in the Playground.
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'))
OP 编辑:
在@Inigo 的帮助和对另一个问题 (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