示例代码
type Base = {
name: string
}
type ExtA = {
address: string
}
type ExtB = {
streetName: string;
}
function handler<T extends Base>(input: T): T {return input}
/** how do I build an object up? */
let payload = {
name: "Aaron"
}
const addA = <T extends Base>(payload: T): T & ExtA => {
return {
...payload,
type: "a",
address: "123 Fake St"
}
}
const addB = <T extends Base>(payload: T): T & ExtB => {
return {
...payload,
type: "b",
streetName: "Main"
}
}
payload = addA(payload)
payload = addB(payload)
// type should be Base & ExtA & ExtB but it isn't
const wrongType = handler(payload)
// ^?
我期望payload
在通过操作函数时改变类型addA
,addB
但事实并非如此。如何让 TS 理解此变量的类型应该改变?
TypeScript 不会对变量状态的任意变化进行建模。虽然在赋值时支持缩小变量范围,但只有当变量的类型为联合类型时才会发生这种情况。因此,如果您将
payload
类型重新分配给 类型X | Y
,并将其值类型为X
,则将payload
缩小为X
。但是,如果您将payload
类型重新分配给 类型,X
并将其值类型为X & Y
,则不会发生缩小。如果想要很好地使用类型系统,则应让每个变量在其生命周期内都具有单一类型。因此,无需重新分配
payload
,只需为每个分配创建一个新变量即可:如果你真的想表示一个变量,其类型会随着时间的推移而变窄,那么你可以使用断言函数来实现,但这些函数有很多注意事项。它们只能通过缩小作为参数传入的变量/属性来工作,并且它们不能返回任何定义的值,因此重新分配仍然不会像你预期的那样缩小。它必须看起来像这样:
这里
addA()
和addB()
是断言函数,它们实际上修改了它们的输入(因为Object.assign()
改变了它的第一个参数)而不是返回任何东西。现在您可以拥有一个
payload
变量,并且每次调用断言函数都会缩小它的范围:这很有效,但除非你真的需要,否则我倾向于避免使用它。如果你决定需要像 a 这样的东西
removeA()
来扩大输入,那么断言函数就无法工作,因为它不是缩小。那么你必须使用不同的变量。游乐场链接到代码