如何让TypeScript推导同一类型的同一属性类型一致(排序场景)
解决方案:基于属性类型的类型守卫实现类型安全排序
要解决这个问题,我们可以通过提取特定类型属性名的工具类型+自定义类型守卫,让TypeScript基于Item的类型定义而非属性值来区分排序逻辑,完全避免冗余的运行时类型检查。
步骤1:定义工具类型筛选属性名
先创建两个工具类型,分别提取Item中类型为string和number的属性名:
type Item = { prop1: string; prop2: number; prop3: string; prop4: number; }; // 提取Item中类型为string的属性名 type StringProps<T> = { [K in keyof T]: T[K] extends string ? K : never }[keyof T]; // 提取Item中类型为number的属性名 type NumberProps<T> = { [K in keyof T]: T[K] extends number ? K : never }[keyof T];
步骤2:实现带类型守卫的排序函数
编写通用排序函数,通过类型守卫判断属性名对应的类型分支,让TypeScript自动推导属性值类型:
function sortItems<T extends Item>(items: T[], propertyName: StringProps<T> | NumberProps<T>): T[] { // 类型守卫:判断属性是否为string类型的属性名 function isStringProp(prop: StringProps<T> | NumberProps<T>): prop is StringProps<T> { // 利用题目中"每个属性类型单一"的约定,仅通过第一个元素做一次类型判断 return typeof items[0][prop] === 'string'; } return [...items].sort((item1, item2) => { const value1 = item1[propertyName]; const value2 = item2[propertyName]; if (isStringProp(propertyName)) { // TypeScript自动推导value1/value2为string类型 return value1.localeCompare(value2); } else { // TypeScript自动推导value1/value2为number类型 return value1 - value2; } }); }
步骤3:使用示例
调用时TypeScript会自动校验属性名,并匹配对应排序逻辑:
const items: Item[] = [ { prop1: "b", prop2: 2, prop3: "x", prop4: 4 }, { prop1: "a", prop2: 1, prop3: "y", prop4: 3 }, ]; // 按string类型属性排序 const sortedByString = sortItems(items, "prop1"); // 按number类型属性排序 const sortedByNumber = sortItems(items, "prop2");
核心原理
- 工具类型
StringProps和NumberProps通过映射类型遍历Item属性,编译期就筛选出对应类型的属性名,让TypeScript提前知晓属性类型归属。 - 自定义类型守卫仅做一次运行时判断(依赖题目中"属性类型单一"的约定),后续排序逻辑中TypeScript会完全信任类型断言,自动推导值的类型,避免对每个元素做冗余类型检查。
内容的提问来源于stack exchange,提问作者Dmitriy Metelya
相关产品推荐
相关产品推荐

