return 相同 T 或不同 R 的值不能接受高阶函数 '( i : T ) => i' 作为默认参数值的通用函数

Generic function that return value of same T or different R can't accept higher order function '( i : T ) => i' as default parameter value

我想了解如何使用泛型类型到达我想要的位置,所以这是我的示例:

function foo(value: 42 = 42 ,bar:(val: any) => any = I => I){
return bar(value)
}

我使用某种类型 Tvalue 在我的例子中类型是 number 所以我使用 42 我想有一个函数作为将值更改为不同类型 R 或 return 相同类型 T 的参数,在这种情况下将是身份 'I => I' 作为默认参数值。

我将我的函数重写为箭头函数,如下所示:

const foo = <R1>(
  value: number = 42,
  bar: <R0>(val: number) => R0 = I => I
) => {
  return bar<R1>(value);
};

const foo0 = (
  value: number = 42,
  bar: <R0>(val: number) => R0 = I => I
) => {
  return bar(value);
};

const foo1 = <R1>(
  value: number = 42,
  bar: (val: number) => R1 = I => I
) => {
  return bar(value);
};


ERROR: Type 'number' is not assignable to type 'RX'. 'RX' could be instantiated with an arbitrary type which could be unrelated to 'number'.

我正在使用 R1R0 因为我不确定在这个例子中 R one 和 R naught 之间的区别但是如果我删除 I => I 默认值错误消息走开,但我不明白为什么或如何克服它...

在下面的示例中 fooB(42,i=>i)foo0B(42,i=>i) 被删除但没有 foo1B(42,i=>i)

如何设置默认值 I => I 而不必指定两者 R | number

const fooB = <R1>(value: number = 42, bar: <R0>(val: number) => R0) => {
  return bar<R1>(value);
};

const foo0B = (value: number = 42, bar: <R0>(val: number) => R0) => {
  return bar(value);
};

const foo1B = <R1>(value: number = 42, bar: (val: number) => R1) => {
  return bar(value);
};


fooB(42,i=>i)
foo0B(42,i=>i)
foo1B(42,i=>i)

您从示例中得到的错误消息是由于在调用函数时可以显式给出类型参数造成的,例如:

fooB<number>(42, (i) => i);

正在考虑 fooB

const fooB = <R1>(value: number = 42, bar: <R0>(val: number) => R0) => {
    return bar<R1>(value);
};

这里发生的是内部 bar 也可以用 R1 以外的任何其他类型调用。如果是这种情况,则无法保证 numbervalue 的类型)是与 R0.

兼容的类型

当我们这样做时就会出现问题,例如:

fooB<21>(42, (i) => i);

关于这个调用我们可以说:R1 被实例化 with/as 21.

这里42 extends number因为它应该考虑value的类型,但是number extends 21是FALSE。换句话说:4221 是类型 number 的不同子类型。因此 returned i 不能分配给 R1R0foo0BR0.

也会出现类似的问题

已经 some good coverage 解释了出现此错误消息的原因。


修复

如果我没理解错的话,你想要一个函数:

  • 接受类型为 T
  • 的参数
  • 可选地接受一个函数fn,它接受一个类型为T的参数和return一个类型为U
  • 的值
  • 使用第一个参数调用 fn,然后 returns 结果 (U) 如果给出 fn,或者 returns T否则(恒等式)。

最直接的写法如下:

function foo<T, U>(value: T, fn: (val: T) => T | U = i => i): T | U {
    return fn(value);
}

但这里foofn确实只能return联合类型T | U,因为只有一个签名可以说明这两种情况

  • 没有fn
  • fn

如果您希望根据是否传递回调来进行类型分支(我假设这是基于默认 I => I),您可以改用 function overloading。本质上就像说 "I have the following 2 signatures for foo":

function foo<T>(value: T): T;
function foo<T, U>(value: T, fn: (val: T) => U): U;
function foo<T>(value: T, fn = (i: T) => i) {
    return fn(value);
}

第一个函数签名用于没有传递回调的情况:它需要一个 T,并且只会 return 一个 T。在这个意义上 foo 就像恒等函数。

第二个签名接受回调fn。如果没有明确给出对 foo 的调用的类型参数,U 将从 fn 的类型推断出来。否则 fn 的类型将根据 U.

的给定类型进行验证

然后你有函数实现。它可以解释两个签名,并使用标识函数(默认值)来匹配第一个签名,或使用给定函数来匹配第二个签名。此 实现签名 仅显式指定所有重载的常量类型,因此仅指定 T。重载将通过在每次调用的基础上确定 return 类型来完成剩下的工作。

这是使用以下示例的完整 TS playground

// Without callback
const a: number = foo(42);             // Fine
const b: string = foo("I'm a string"); // Fine
const c: string = foo(42);             // ERROR: type '42' is not assignable to type 'string'

// With callback
const d: number = foo('42', parseInt);                  // Ok
const e: string = foo(42, (x: number) => x.toString()); // Ok
const f: string = foo(42, (x: number) => x + 1);        // ERROR: type 'number' is not assignable to type 'string'

// With explicit types
const g: number = foo<string, number>('42', parseInt);  // Ok
const h: number = foo<string, number>('42', (x) => x);  // ERROR: type 'string' is not assignable to type 'number'