如何为 TypeScript 中的子类型创建通用转换器函数(无需类型断言)?

问题描述 投票:0回答:1

我正在开展一个个人项目,学习使用 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 错误的函数的通用版本?如何创建一个一般更新任何对象类型的属性的转换器?

typescript generics functional-programming fp-ts
1个回答
0
投票

您可以使用两个泛型(

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
或其他东西一起使用。

© www.soinside.com 2019 - 2024. All rights reserved.