You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

映射类型中严格Flavored键类型的TypeScript技术问题

问题根源

你使用的Flavored(品牌化)类型本质是string的子类型,但TypeScript中对象键仅支持原始类型(string/number/symbol),因此无法在类型层面直接区分UserId和UserName作为键的差异。同时你定义的ColorsByUserId交叉了包含number/symbol的索引约束,导致与对象字面量的string索引签名冲突;访问错误键时,TypeScript会回退到string类型推断,无法识别非法键,因此返回any。


解决方案

方案1:工厂函数+类型守卫(推荐,支持运行时验证)

通过工厂函数约束对象创建时的键类型,配合类型守卫限制访问,同时可添加运行时校验逻辑:

declare const flavor: unique symbol;
type Flavored<T, F> = T & { readonly [flavor]?: F };

type UserId = Flavored<string, 'UserId'>;
type UserName = Flavored<string, 'Name'>;
type Color = Flavored<string, 'Color'>;

const alice: UserName = 'alice';
const id: UserId = 'user-0001';
const blue: Color = 'blue';
const yellow: Color = 'yellow';

// 工厂函数:确保输入对象仅包含UserId键
function createColorsByUserId(data: Record<UserId, Color[]>): Record<UserId, Color[]> {
    return data;
}

// 合法赋值:无报错
const favorites = createColorsByUserId({
    [id]: [blue, yellow],
});

// 非法赋值:此处会报错(alice是UserName类型)
// const invalidFavorites = createColorsByUserId({
//     [alice]: [blue],
// });

// 类型守卫:判断值是否为合法UserId(可添加运行时格式校验)
function isUserId(value: string): value is UserId {
    return value.startsWith('user-');
}

// 安全访问函数:仅接受UserId作为参数
function getUserFavorites(colors: Record<UserId, Color[]>, id: string): Color[] | undefined {
    return isUserId(id) ? colors[id] : undefined;
}

// 合法访问:无报错
getUserFavorites(favorites, id);

// 非法访问:此处会报错(alice无法通过类型守卫)
// getUserFavorites(favorites, alice);

方案2:纯类型层面约束(依赖TS 4.1+)

利用索引重映射调整类型定义,避免索引冲突,同时通过as const让TypeScript推断具体键类型:

declare const flavor: unique symbol;
type Flavored<T, F> = T & { readonly [flavor]?: F };

type UserId = Flavored<string, 'UserId'>;
type UserName = Flavored<string, 'Name'>;
type Color = Flavored<string, 'Color'>;

const alice: UserName = 'alice';
const id: UserId = 'user-0001';
const blue: Color = 'blue';
const yellow: Color = 'yellow';

// 自定义严格记录类型:仅允许指定键,其他string键返回never
type StrictRecord<K extends string, V> = Record<K, V> & {
    [key in string as Exclude<key, K>]: never;
};

type ColorsByUserId = StrictRecord<UserId, Color[]>;

// 合法赋值:使用as const让TS推断具体键类型
const favorites: ColorsByUserId = {
    [id]: [blue, yellow],
} as const;

// 非法访问:此处会报错(alice不是UserId类型)
// for (const color of favorites[alice]) {
//     console.log(color);
// }

内容的提问来源于stack exchange,提问作者Nycki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 09:40:54