啊,欢迎来到微软/TypeScript#13995 https://github.com/microsoft/TypeScript/issues/13995 and 微软/TypeScript#24085 https://github.com/microsoft/TypeScript/issues/24085。目前,编译器不使用控制流分析来缩小泛型类型参数或依赖于泛型类型参数的类型值的范围。从技术上讲,缩小类型参数本身是错误的,因为有人可以通过这种方式调用通用版本:
const filter: Filter = Math.random() < 0.5 ?
{ type: "tickList", value: "str" } : { type: "color", value: colorValue };
const newValue: string | ColorValueType = Math.random() < 0.5 ? "str" : colorValue;
onValueChange(filter, newValue); // no compiler error
// const onValueChange: <Filter>(filter: Filter, newValue: string | ColorValueType) => void
编译器会推断Filter
for T
,根据通用签名,这是“正确的”,但它会导致与非通用版本相同的问题。在函数内部可能是这样的newValue
不匹配filter
. ????
上述 GitHub 问题中已经有许多关于处理此问题的建议和讨论。如果像微软/TypeScript#27808 https://github.com/microsoft/TypeScript/issues/27808,你可以这样说T extends-exactly-one-member--of Filter
意思是T
可能TickListFilter
or ColorFilter
但它可以not be Filter
。但现在还没有办法说这个。
目前最简单的方法是继续(假设您不想实际检查函数的实现内部filter
and newValue
彼此匹配)将涉及类型断言 https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions或类似的东西。您需要充分放松类型检查以允许代码,并且只是期望/希望没有人会像我上面那样调用您的函数。
这是使用类型断言的一种方法:
const onValueChange = <T extends Filter>(filter: T, newValue: T["value"]) => {
const f: Filter = filter; // widen to concrete type
if (filter.type === "tickList") {
const v = newValue as TickListFilter["value"]; // technically unsafe narrowing
f.value = v; // okay
} else {
const v = newValue as ColorFilter["value"]; // technically unsafe narrowing
f.value = v; // okay
}
}
Playground 代码链接 https://www.typescriptlang.org/play/#code/PTAqE8AcFMAIBNoGMA2BDATmgLgSwPYB2AXAFC6HbQYBmaScouSA1gDK4DO2AYrilQywA3qVixsUaMVgAiPKw7dZAbjGwAbmhQBXabG4YKAczUBfUuUrU6DWAGF8KfBj4DqI9ZJgzZSJy6y6lq6+o7OGABq2noQMKQWpN5w4S7RoXFwALwisP4RMoYmsGZq-oTceQFRMWHV6bFSsDnCVQVyaACOOmgAtpgU0LIlaklNboLNsEyKXLz8kwA+DtUT1KPllUQN0PYAFmiExtmwADygsNAAHlSE8JywaxgAfAAUNAvUMqAANLCE0AA7jtvgBtWQhPSyAC6AEpms9POJNthYDQZE8ph93BgVLAQLBAbhEIQJPgqoQkBhoFQJFJ1LgaLB3p8MAA6ZLNLI5eTMdhzWTw0TiZFESoaKYA4G1WBoB4zfncJ7gyFDaF4glUJB7QjMbQocCwHQVNA0OCETAYfBEo7qcQ0NmqqYaDXAWD4FhocDqMyXFCcODCkUozSSoE7WUPVKuVkq2ow10SZA6vUoA1Gk1m-6W60mNQitGOmU5F34t0er0+hKWEPYwQY1lTACyOD2bKwd3wvVe8NOsAADGyAKywAD86layV8CkV2Fkf1VvkMw19MknUl8+UCC9qMi3NVCI1IIalIIM2CMR1gy2jO0yzdb7cO8C7PbOA+HY7ky9ge-qtTUbZan2Q5jhZHE-lPWpYUTQhyX8XpIH4DxqCtDBSAJEMgNCECjn0U4njeOsvkeVlIPDXdz0vYxrxWCI7ykeEskRDR8GJSwgA