我正在开展一个个人项目,学习使用 TypeScript 进行函数式编程,并尝试使用管道函数和高阶函数来以富有表现力的方式创建变压器。我在 TypeScript 类型方面遇到了一些问题,尤其是在尝试创建一个泛型函数,将某种类型的某个对象的转换器返回到该对象的子类型时。
这是设置:
type Cat = {
whiskers: number;
}
type FancyCat = Cat & {
tophatSize: number;
}
type PartyCat = Cat & {
balloons: number;
}
我想创建一个函数,该函数通常创建一个转换器来更新任何对象上的任何属性,将对象转换为该对象的子类型(添加属性)。
我可以使用硬编码函数来做到这一点:
const createFancyCatTransformer = (props: { tophatSize: number }) => (cat: Cat) => {
return {
...cat,
...props
}
}
const cat = {
whiskers: 50
}
const fancyCat = pipe(cat, createFancyCatTransformer({ tophatSize: 5 }));
但是,我想让这个通用。我想要一个像
createFancyCatTransformer
这样的通用函数,而不是 createTransformerToSubtype<Cat, FancyCat>({ tophatSize: 5 })
。
我无法让这个工作,所以为了进一步简化,我决定尝试制作一个变压器来一般地将
Cat
扩展到它的一个子类型,但我也遇到了问题。
这是我为 Cat 制作通用变压器的尝试:
const createCatExtender =
<E extends Cat>(extension: Omit<E, keyof Cat>) =>
(originalObject: Cat): E => {
return { ...originalObject, ...extension };
};
// Usage
createCatExtender<PartyCat>({ balloons: 7 });
但是,我收到以下 TypeScript 错误:
Type '{ whiskers: number; } & Omit<E, "whiskers">' is not assignable to type 'E'.
'{ whiskers: number; } & Omit<E, "whiskers">' is assignable to the constraint of type 'E', but 'E' could be instantiated with a different subtype of constraint 'Cat'
不言而喻,我不想使用任何类型断言或类似的东西。如果我在 return 语句中断言“as E”,我的示例将有效,但我想避免这种情况。
是否可以创建一个没有 TypeScript 错误的函数的通用版本?如何创建一个一般更新任何对象类型的属性的转换器?
您可以使用两个泛型(
B
用于基础,E
用于扩展)和实用程序RemoveProperties
:
type Cat = {
whiskers: number;
}
type FancyCat = Cat & {
tophatSize: number;
}
type PartyCat = Cat & {
balloons: number;
}
type RemoveProperties<T, U> = Omit<T, keyof U>;
const createTransformer = <
B extends {},
E extends B>(extension: RemoveProperties<E, B>
) => {
return (base: B) => {
return { ...base, ...extension } as E;
// as E is optional but it will be cleaner than B & RemoveProperties<E, B>
}
}
然后你就可以这样食用:
const fancyCatTransformer = createTransformer<Cat, FancyCat>({ tophatSize: 3 });
const partyCatTranssformer = createTransformer<Cat, PartyCat>({ balloons: 7 });
const baseCat = { whiskers: 3 };
const fancyCat = fancyCatTransformer(baseCat);
console.log(
fancyCat.tophatSize, // OK
fancyCat.whiskers, // OK
fancyCat.balloons // Error
);
const partyCat = partyCatTranssformer(baseCat);
console.log(
partyCat.tophatSize, // Error
partyCat.whiskers, // OK
partyCat.balloons // OK
);
createTransformer
函数是通用的,因此您可以将其与Cat
或其他东西一起使用。