搜索词
- 缩小类型
建议
目前,TypeScript无法根据创建新对象时定义的其他属性来缩小类型。例如,假设我们有以下函数:
type TParams = { kind: 'name', value: string } | { kind: 'age', value: number }
const example = (params: TParams) => {
...
}
我们可以使用类似以下代码调用它:
example({ kind: 'name', value: 'macabeus' })
example({ kind: 'age', value: 23 })
但是,编译像这样的代码是不可能的:
let nameOrAge: 'name' | 'age' = 'name'
example({
kind: nameOrAge,
value: (nameOrAge === 'name' ? 'macabeus' : 23),
})
- Playground*
但我们知道,如果 nameOrAge
是 'name'
,那么属性 value
将是一个字符串,否则 nameOrAge
将是 'age'
,而 value
将是 23
-这是正确的。
我认为如果TS能检查这种行为会很有用。
用例
在上面描述的上述情况中减少代码量是有用的。
目前,为了获得相同的行为,必须编写更多的代码:
let nameOrAge: 'name' | 'age' = 'name'
if (nameOrAge === 'name') {
example({
kind: nameOrAge,
value: 'macabeus',
})
} else {
example({
kind: nameOrAge,
value: 23,
})
}
- Playground*
相关
我知道有很多关于基于变量缩小类型的问题的讨论,例如:
但在这里我只说的是创建新对象时的情况,而且据我所知,与这些其他问题相比,这是一个更简单的情况,因为不需要跟踪整个代码库。
在我提到的情况下,TS只需要检查在同一对象上定义的其他属性 以及 变量名是否已经被使用。例如,TS不需要在这种情况下改变其行为:
example({
kind: nameOrAge,
value: otherVariabledDeclaredOnOtherScope ? 'macabeus' : 23,
}) // still should fail
也许这不是最好的解决方案,但我认为它遵循帕累托效率原则。
检查表
我的建议满足以下准则:
- 这不会对现有的TypeScript/JavaScript代码造成破坏性的变化
- 这不会改变现有JavaScript代码的运行时行为
- 这可以在不根据表达式的类型生成不同的JS的情况下实现
- 这不是一个运行时特性(例如库功能、JavaScript输出的非ECMAScript语法等)
- 这个特性将与其他 TypeScript's Design Goals 保持一致。
3条答案
按热度按时间t3irkdon1#
这是一个特定于 #12184 的使用案例,类似于我为 #25051 提出的一个激励示例(参见此评论)。
这里似乎建议的是,每当创建一个新的对象字面量时,编译器应该检查是否有任何联合类型的值被多次提及,然后自动地将控制流分析分布在该值的不同缩小版本上。因此,如果你有以下代码:
declare const x: A | B | C;
declare const f: (t: T) => F;
const obj = {x: x, fx: f(x)};
你希望编译器能注意到
x
在obj
中被多次使用,然后依次缩小x
为A
、B
和C
,在每种情况下计算obj
的类型,然后将它们合并为{x: A, fx: F<A>} | {x: B, fx: F<B>} | {x: C, fx: F<C>}
。如果这样的自动分析能够发生,那将太好了,但我预计性能影响会是不可接受的,尤其是考虑到类似
{x: x, y: y, z: z, fxyz: f(x, y, z)}
的组合问题。在我幻想的世界(即 #25051)里,我希望能够手动请求编译器进行这样的分析,以便在保留正常代码的行为和性能的同时获得这种好处。在你的情况下,它看起来可能像这样:
example({
kind: nameOrAge,
value: (nameOrAge === 'name' ? 'macabeus' : 23),
} as if switch(nameOrAge))
abithluo2#
感谢您的回复!
看起来这里的建议是,每当创建一个新的对象字面量时
是的。这个提案的想法是在创建一个新的对象字面量时进行检查,以避免性能问题并使其更容易实现。
我遵循帕累托原则。我知道这不是最佳方法,但它非常简单。
编译器应该检查是否有任何联合类型的值被多次提及,然后自动将控制流分析分布在该值的不同缩小版本上
我不知道是否真的需要为枚举上的每个值自动分配。例如,在这种情况下只需要分配两次:
只需要检查:
nameOrSomething
是'name'
且value
是'macabeus'
时,这个对象是否可以在example
调用中使用?nameOrSomething
是'age' | 'height' | 'money'
且value
是20
时,这个对象是否有效?您是否在寻找其他需要进行更多检查的情况?
wh6knrhe3#
可处理案例的存在并不意味着组合爆炸性案例仍然不存在。算法不能是“寻找判别式的一次比较,然后做正确的事情”。
如果你写了类似这样的代码:
然后将其与
example
的复杂定义进行比较,对我来说,很明显你如何避免多次迭代整个并集。考虑到人们在 JSX 中生成的属性类型,这不是一个不切实际的场景。