interface InterfaceOne {
fieldOne: string
}
interface InterfaceTwo {
fieldTwo: string
}
enum Types {
One = 'one',
Two = 'two'
}
type TypeMap = {
[Types.One]: InterfaceOne
[Types.Two]: InterfaceTwo
}
export default class TestState<T extends Types> {
private baseType: T
private interfaceType: TypeMap[T] | null
constructor(testFieldOne: T, testFieldTwo: TypeMap[T]) {
this.baseType = testFieldOne
this.interfaceType = testFieldTwo
}
private init(): void {
if (this.baseType === Types.Two) {
const testObject: InterfaceTwo = { fieldTwo: 'test' }
console.log(this.interfaceType)
this.interfaceType = testObject // ❌ TypeScript infers TypeMap[T] as 'InterfaceOne & InterfaceTwo' instead of `InterfaceOne | InterfaceTwo`
}
}
}
我尝试过的是:
解释铸造
this.interfaceType = testObject as TypeMap[Types.Two]
但错误仍然存在。
conding typemap是一个联合(|),而不是交集(&) Typemap定义似乎正确,但是Typescript仍会侵入交叉点。任何洞察力或解决方法都将不胜感激!
歧视的联合type;确实,它根本不是
union类型TestState<T>
不能缩小明显类型的
this.baseType === Types.Two
,因此不知道
this
具有类型this.interfaceType
。 您可以决定尝试告诉TypeScript,通过给出一个
将是
this
方法内部的歧视联合类型TestState<Types.One> | TestState<Types.Two>
,这将使init()
编译无错误::
this
但是,除非已经缩小到这样的结合,否则您无法轻易从任何地方开始。
init()
因此,我们要做的就是将问题移动。
最终,您不能真正同时使用ControlFlow Analysis和
基因。好吧,在打字稿5.9中,我们很可能会看到microsoft/typeScript#61359启用了一些通用重新限制,但这可能对此示例无济于事。
因此,我们应该放弃控制流分析或仿制药。由于类不能直接是工会,因此我们需要仿制药,因此让我们尝试从控制流分析中重构。一种方法是将诸如
private init(this: TestState<Types.One> | TestState<Types.Two>): void {
if (this.baseType === Types.Two) {
const testObject: InterfaceTwo = { fieldTwo: 'test' }
console.log(this.interfaceType)
this.interfaceType = testObject // okay
}
}
之类的检查替换为单个通用索引,以替换为像
init()
这样的“处理”对象。这有类似的效果,但是现在您可以说出类型正在发生的事情,而不是遵循
this
/constructor(testFieldOne: T, testFieldTwo: TypeMap[T]) {
this.baseType = testFieldOne
this.interfaceType = testFieldTwo
this.init(); // error!
// 'TestState<T>' is not assignable to 'TestState<Types.One> | TestState<Types.Two>'
}
控制流。
在这里是这样做的方法:
if (obj.discrim === "x") { } else if (obj.discrim === "y") else
const process = {x: ()=>{}, y: ()=>{}}; process[obj.discrim]()
的类型是el图上的映射类型
,其中每个属性都是在相应的if
上运行的函数。因此,有一个else
方法可以期望private init(): void {
const process: { [P in Types]?: (t: TestState<P>) => void } = {
[Types.Two](thiz) {
const testObject: InterfaceTwo = { fieldTwo: 'test' }
console.log(thiz.interfaceType)
thiz.interfaceType = testObject
}
}
process[this.baseType]?.(this);
}
作为一个论点。我命名了该参数process
,因为它代替了您的示例。 由于已知从一开始就是一个
P
,因此您可以进行分配。
,然后为了实际上使用,您需要访问
Types
并调用它(如果存在,则将其作为参数为and的chate链(TestState<P>
))。因此,我们有
Types.Two
,因为
TestState<Types.Two>
具有通用类型thiz
this
具有类型thiz
。 Typescript能够将
TestState<Types.Two>
视为适当的单函数类型的事实,而not是两个函数的结合(因此,仅接受参数的启动
的功能是这项工作的核心原因,您可以在microsoft/typecript#47109
.indercland#47109.inderction上阅读有关此功能的核心原因
没有,这样做的事情不是很直观。但是,直到又一次,除非打字稿可以将控制流量分析和通用工作无缝地一起进行,否则您需要跳过一些篮球以获取打字稿以了解您的工作。
当然,如果您不太在乎Typescript认为您在做什么,只是希望它能
accept,您总是可以使用
typeessertions(您称之为“铸造”),就像:
process
向process[this.baseType]
type typey或多或少只是告诉打字稿,它甚至不应该尝试检查您的工作。这是权宜之计,但显然不如重构安全。 这完全取决于您的优先事项。
to代码的链接链接