TypeScript泛型实现对象仅使用指定联合类型键、无需全量键且属性类型确定
需求说明
现有已知值的联合类型:
type Possibilities = "a" | "b" | "c"
需要实现类型约束,使得创建的对象仅能使用属于该联合类型的成员作为键,但无需包含全部键;同时要避免属性类型被推导为string | undefined。
该类型用于固定值的常量声明,TypeScript本身可对常量完成正确的类型推导:
符合约束的合法示例如下:
const mine = { a: "this", b: "and", c: "that" } const yours = { b: "it" }
不符合约束的非法示例如下:
// 存在不在联合类型中的未知键 'x' const theirs = { a: "these", x: "those" } // 优先禁止空对象 const noOnes = {}
已尝试方案
- 使用
Record<Possibilities, string>或索引签名写法{[key in Possibilities]: string}标注类型时:
const mine: Record<Possibilities, string>; const mine: {[key in Possibilities]: string};
TypeScript会要求对象必须包含所有指定键,不符合需求。
- 使用
Partial<Record<Possibilities, string>>或可选索引签名写法{[key in Possibilities]?: string}标注类型时:
const mine: Partial<Record<Possibilities, string>>; const mine: {[key in Possibilities]?: string};
虽然允许仅传入部分键,但属性类型会被推导为string | undefined,即使给对象加上as const断言也无法解决该问题。
- 对每个声明的对象单独使用
Pick<>或Omit<>虽然可以得到预期结果,但使用成本极高,存在重复编码问题(需要在类型定义和值声明中重复书写"a", "b"...等值)。
同时该方案下Omit<>甚至无法触发可选值的自动补全,当需要使用大部分可选键时,需要手动Pick大量值或盲目Omit,极易出错。
目前猜测解决方案需要借助继承目标类型的泛型实现,但暂时没有找到正确的实现方式。
补充编辑
现有一个接近可用的方案实现如下:
type AtLeastOne<T, U = {[K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U] type Possibilities = "a" | "b" | "c" let mine: AtLeastOne<Record<Possibilities, string>>;
该方案可以强制对象至少包含a/b/c中的一个属性,禁止传入未知属性,且已声明属性的类型不会被推导为“可能为undefined”:


但该方案存在缺陷:当对象设置多个允许的属性时,类型校验会报错:
内容的提问来源于stack exchange,提问作者Jonatas Amaral
相关产品推荐
相关产品推荐

