如何编写TypeScript函数实现非标准判别式联合类型收窄?
问题描述
我需要构建一个TypeScript函数,对运行时表现为判别式联合但类型定义不属于判别式联合的联合类型执行类型收窄。目前代码中,if语句内的props始终保持WidgetPropsSet类型,无法自动收窄:
if (props.kind === "CheckboxInput") { console.log("We need to use", props, "here as its narrowed `CheckboxInput` type"); } if (props.kind === "DropdownInput") { console.log("We need to use", props, "here as its narrowed `DropdownInput` type"); } // ... 另有16个类似分支
(完整代码为Svelte模板,无法重构现有结构)我想用一个函数替换props.kind === "discriminator"的判断逻辑,伪代码如下:
function narrowWidgetProps(props: WidgetPropsSet, kind: WidgetPropsNames): a-concrete-variant-from-WidgetPropsSet | undefined { if (props.kind === kind) return a-concrete-variant-from-WidgetPropsSet; else return undefined; }
请问如何将该伪代码编写为可实现类型收窄的标准TypeScript函数?
相关上下文代码
抽象类与子类定义
abstract class WidgetProps { kind!: string; } class CheckboxInput extends WidgetProps { checked!: boolean; disabled!: boolean; // ... } class DropdownInput extends WidgetProps { entries!: string[]; selectedIndex!: number | undefined; // ... }
联合类型定义(需保持{ value, name }[]格式以适配第三方代码)
const widgetSubTypes = [ { value: CheckboxInput, name: "CheckboxInput" }, { value: DropdownInput, name: "DropdownInput" }, // ... ] as const; type WidgetPropsSet = InstanceType<typeof widgetSubTypes[number]["value"]>; type WidgetPropsNames = typeof widgetSubTypes[number]["name"];
解决方案
核心思路是通过泛型关联kind参数与具体类型变体,结合映射类型建立字符串标识和实例类型的绑定,实现安全的类型收窄。
1. 创建关联映射类型
基于widgetSubTypes生成一个映射类型,将每个name字符串绑定到对应的实例类型:
type WidgetPropsMap = { [K in typeof widgetSubTypes[number]["name"]]: InstanceType<Extract<typeof widgetSubTypes[number], { name: K }>["value"]> };
该类型会生成类似{ CheckboxInput: CheckboxInput; DropdownInput: DropdownInput }的结构,明确kind值与具体类型的对应关系。
2. 实现类型收窄函数
利用泛型约束让返回类型与传入的kind参数关联,同时通过类型断言保证运行时类型安全:
function narrowWidgetProps<K extends WidgetPropsNames>( props: WidgetPropsSet, kind: K ): WidgetPropsMap[K] | undefined { if (props.kind === kind) { return props as WidgetPropsMap[K]; } return undefined; }
3. 使用示例
调用函数后,TypeScript会自动收窄返回值的类型:
const narrowedCheckbox = narrowWidgetProps(props, "CheckboxInput"); if (narrowedCheckbox) { // 此处narrowedCheckbox类型为CheckboxInput,可直接访问checked、disabled等属性 console.log(narrowedCheckbox.checked); } const narrowedDropdown = narrowWidgetProps(props, "DropdownInput"); if (narrowedDropdown) { // 此处narrowedDropdown类型为DropdownInput,可直接访问entries、selectedIndex等属性 console.log(narrowedDropdown.entries); }
原理说明
- 泛型
K绑定了传入的kind参数,让TypeScript推导返回类型为对应kind的具体实例类型; - 运行时通过
props.kind === kind的判断,确保类型断言props as WidgetPropsMap[K]的安全性; - 返回
undefined的分支配合if判断,让TypeScript自动处理可选类型的收窄。
内容的提问来源于stack exchange,提问作者Keavon
相关产品推荐
相关产品推荐

