类型'(s:字符串)=>字符串'不能分配给类型'<T>(t:T)=> T'

时间:2020-11-12 13:35:04

标签: javascript node.js typescript

我这样声明了Typescript接口:

interface ProductFieldMapping {
  name: string,
  mapping: (p: Product) => string | number,
  formatting: <T>(t: T) => T,
}

还有一个返回此类字段列表的函数,如下所示:

export const mappingFunctions = (host: string): ProductFieldMapping[] => {
  const mappings = defaultFunctions(host)

  return [
    {
      name: 'title',
      mapping: mappings.productName,
      formatting: (s: string) => s,
    },
  ]
}

但是当我要分配格式此简单功能(s: string) => s

时,会出现此错误
Type '(s: string) => string' is not assignable to type '<T>(t: T) => 
T'.

我想念什么?

可能的解决方法:

type MappingResult = | string | number

interface ProductFieldMapping<T> {
  name: string,
  mapping: (p: Product) => T,
  formatting: (t: T) => T,
}

export const mappingFunctions = (host: string): ProductFieldMapping<MappingResult>[] => {
  const mappings = defaultFunctions(host)

  return [
    {
      name: 'title',
      mapping: mappings.productName,
      formatting: compose([limitSize(255), replaceNewLine(' ')]),
    }]
}

2 个答案:

答案 0 :(得分:1)

在调用函数时需要提供类型。
对于您的情况,应将string替换为T
因为您只分配了函数,而不调用它。

formatting: <T>(s: T) => s,

然后在调用该函数时,然后为该函数指定一个类型。

formatting<string>("1") // it doesn't throw error
formatting<number>(1) // it doesn't throw error

编辑

我看到文档中有一章涉及Generic Classes
VLAZ有相同的方法,但是文档使用类。

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
  
let myGenericString = new GenericNumber<string>();
myGenericString.zeroValue = "0";
myGenericString.add = function(x, y) {
    return x + y.toLocaleLowerCase(); // it works
};

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {
    return Math.round(y) // it works
};

答案 1 :(得分:1)

您可以使用ProductFieldMappings使用discriminated unionalso see here)来加强类型。

有区别的联合是您在多个类型上具有联合,但可以使用它们上的属性来缩小类型的范围。

interface ProductFieldMapping<T> {
  name: keyof Product,
  mapping: (p: Product) => T,
  formatting: (t: T) => T,
}

interface StringFieldMapping extends ProductFieldMapping<string> {
    type: "string";
}

interface NumberFieldMapping extends ProductFieldMapping<number> {
    type: "number";
}

type Mapping = StringFieldMapping | NumberFieldMapping;

这使ProductFieldMapping成为通用接口,其中mappingformatting的结果绑定在一起,并且必须使用相同的格式。由于我们只有两种格式,因此我们为每种类型创建一种-StringFieldMappingNumberFieldMapping。它们每个都有一个独特的type,因此Mapping现在是一个有区别的联合。

从这里您可以生成Mappings的数组,您会知道它们使用正确的数据:

const numFormat = (n: number) => Math.round(n);
const strFormat = (s: string) => s.toLowerCase();

export const mappingFunctions = (host: string): Mapping[] => {
  const mappings = defaultFunctions(host);

  return [
    {                                   // OK
      name: 'title',
      type: "string",                   // it's a string
      mapping: mappings.productName,    // produces string ✔
      formatting: strFormat,            // consumes string ✔
    },
    {                                   // OK
      name: 'price',
      type: "number",                   // it's a number 
      mapping: mappings.price,          // produces number ✔
      formatting: numFormat,            // consumes number ✔
    },
    {                                   // ERROR
      name: 'a',
      type: "string",                   // it's a string
      mapping: mappings.productName,    // produces string ✔
      formatting: numFormat,            // consumes number ❌
    },
    {                                   // ERROR
      name: 'b',
      type: "number",                   // it's a number
      mapping: mappings.price,          // produces number ✔
      formatting: strFormat,            // consumes string ❌
    },
    {                                   // ERROR
      name: 'c',
      type: "string",                   // it's a string
      mapping: (p: Product) => 42,      // produces number ❌
      formatting: strFormat,            // consumes string ✔
    },
    {                                   // ERROR
      name: 'd',
      type: "number",                   // it's a number
      mapping: (p: Product) => "hello", // produces string ❌
      formatting: numFormat,            // consumes number ✔
    },
  ]
}

Playground Link