简体   繁体   English

嵌套函数的打字稿泛型推断

[英]Typescript generic infer of nested function

I have these ts function:我有这些 ts 功能:

const fnGeneric = <V,I>(fn:<U>(param:U) => V, param:I) => fn(param);
const fn = (some:string) =>  some;
const result = fnGeneric(fn,5);

but result ends with static type error :但结果以静态类型错误结束:

Argument of type '(some: string) => string' is not assignable to parameter of type '(param: U) => string'. '(some: string) => string' 类型的参数不能分配给 '(param: U) => string' 类型的参数。 Types of parameters 'some' and 'param' are incompatible.参数“some”和“param”的类型不兼容。 Type 'U' is not assignable to type 'string类型 'U' 不能分配给类型 'string

What is wrong with this pattern?这种模式有什么问题? I think U should infer I type as number, but I have some blank space here.我想你应该推断我输入的是数字,但我这里有一些空格。

I think your definition is off, the types will not be inferred automatically to fit your intent.我认为您的定义已关闭,不会自动推断类型以符合您的意图。 This is the definition i would use:这是我将使用的定义:

const fnGeneric = <V,U>(fn: (param:U) => V, param: U) => fn(param);
const fn = (some: string) =>  some;
const result = fnGeneric(fn, 5);

This uses the same type parameter U both in the function and the second argument, ensuring their compatibility.这在函数和第二个参数中使用相同的类型参数U ,确保它们的兼容性。 Of course the call will be invalid then, because fn only accepts strings as argument.当然调用将是无效的,因为fn只接受字符串作为参数。

The reason for this becomes more clear if you look at the type signature of fnGeneric as it would appear in a .d.ts file.如果您查看fnGeneric的类型签名,就像它出现在.d.ts文件中一样,其原因就会变得更加清楚。

declare const fnGeneric: <V,I>(fn:<U>(param:U) => V, param:I) => V

Does not relate U and I in any way.不以任何方式将UI联系起来。 Here is what the type checker can infer from this signature:以下是类型检查器可以从该签名中推断出的内容:

  1. fnGeneric is a function defined for all types V , and I , receiving 2 parameters fn and param and returning the type V fnGeneric为所有类型VI定义的函数,接收 2 个参数fnparam并返回类型V
  2. fn is a function defined for all types U receiving 1 parameter param , of type U and returning V fn为所有类型U定义的函数接收 1 个参数param ,类型U并返回V
  3. param is of type I paramI

This type-checks fine, because fn is a function that can turn any type into a V , so passing it an I in the definition fn(param) is perfectly acceptable.这种类型检查很好,因为fn是一个可以将任何类型转换为V的函数,因此在定义fn(param)传递一个I是完全可以接受的。

The problems start when you try to call this function.当您尝试调用此函数时,问题就开始了。 In order to call fnGeneric , you need to give is a function that can take any type , and turn it into the type you want to get out of fnGeneric .为了调用fnGeneric ,你需要给出一个可以接受任何类型的函数,并将它变成你想要从fnGeneric取出的fnGeneric This is impossible!这是不可能的! Passing in a const fn: (some: string) => string like in your example doesn't work, becase fn doesn't accept any type, it only accepts strings.传入一个const fn: (some: string) => string就像在你的例子中一样不起作用,因为fn不接受任何类型,它只接受字符串。 The signature would need to be const fn: <U>(some: U) => string .签名需要是const fn: <U>(some: U) => string Hence the error:因此错误:

Argument of type '(some: string) => string' is not assignable to parameter of type '(param: U) => string'. '(some: string) => string' 类型的参数不能分配给 '(param: U) => string' 类型的参数。 Types of parameters 'some' and 'param' are incompatible.参数“some”和“param”的类型不兼容。 Type 'U' is not assignable to type 'string类型 'U' 不能分配给类型 'string

Additionally, based on your example fnGeneric(fn, 5) , you are trying to pass a number to a function that takes a string by coercing fn to be a function that takes any type of argument.此外,根据您的示例fnGeneric(fn, 5) ,您试图通过将fn强制为采用任何类型参数的函数,将number传递给采用string的函数。 Here is a code snippet that type checks:这是一个类型检查的代码片段:

const fnGeneric = <A>(fn: <B>(param: B) => B, param: A): A => fn(param);
const fn = <A>(some: A): A => some;
const result: number = fnGeneric(fn, 5); // result = 5

fn in this example is commonly known as the identity function - which returns the parameter it is given, and it is the only valid implementaion (without arbitrary type casting) of a function with the signature <A>(a: A) => A .这个例子中的fn通常被称为identity函数——它返回给定的参数,它是具有签名<A>(a: A) => A的函数的唯一有效实现(没有任意类型转换) . By extension, fnGeneric is a function that takes the identity function and a parameter of any type, and returns the application of the identity function to that parameter.通过扩展, fnGeneric是一个函数,它接受恒等函数和任何类型的参数,并将恒等函数的应用返回给该参数。

What is wrong with this pattern?这种模式有什么问题?

fnGeneric as defined doesn't make any sense, it would be impossible to write a program that type checks that satisfies the signature.定义的fnGeneric没有任何意义,编写满足签名的类型检查的程序是不可能的。 The only way to make the types work simply makes it redundant.使类型工作的唯一方法只是使它变得多余。 There aren't any cases where it would be preferable to call fnGeneric(identity, x) instead of just identity(x) (or for that matter, simply the expression x ).没有任何情况下最好调用fnGeneric(identity, x)而不是identity(x) (或者就此而言,只是表达式x )。 They are all completely equivalent.它们都是完全等效的。

i can give u more realistic scenario, i made simplier version of fn, there is real:我可以给你更真实的场景,我制作了更简单的 fn 版本,有真实的:

export const inject = <I,V>(fn:<U>(input?:U) => V, resolveWithPayload: boolean, resolveArgs?: I) => <R>(payload:R):R => { resolveWithPayload ? fn(payload) : resolveArgs ? fn(resolveArgs) : fn(); return payload; };

const fn = (value:number):number => {
    propertyToMutate = value;
    return propertyToMutate;
}

const res =_fish.inject(fn,false,60)(50);

but calling it ends with :但调用它以:

Argument of type '(value: number) => number' is not assignable to parameter of type '(input?: U) => number'. “(value: number) => number”类型的参数不能分配给“(input?: U) => number”类型的参数。 Types of parameters 'value' and 'input' are incompatible.参数“值”和“输入”的类型不兼容。 Type 'U' is not assignable to type 'number'. “U”类型不能分配给“数字”类型。

if i improve code by your way:如果我按照你的方式改进代码:

export const inject = <I,V,U>(fn:(input?:U) => V, resolveWithPayload: boolean, resolveArgs?: I) => <R>(payload:R):R => { resolveWithPayload ? fn(payload) : resolveArgs ? fn(resolveArgs) : fn(); return payload; };

it ends by type definition error in inject itself like:它以注入自身的类型定义错误结束,例如:

TS2345: Argument of type 'R' is not assignable to parameter of type 'U'. TS2345:“R”类型的参数不能分配给“U”类型的参数。

TS2345: Argument of type 'I' is not assignable to parameter of type 'U'. TS2345:“I”类型的参数不能分配给“U”类型的参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM