打字稿方法 return 基于可选参数 属性 函数的类型
Typescript method return type based on optional param property function
尝试创建一个简单的实用程序:
- return 按原样在给定数组中
- 或根据给定的可选参数进行转换。
这是代码:
type MapperFn<T, U> = (val: T) => U;
interface mapperOpts<T,U> {
cb?: MapperFn<T,U>
}
interface mapper {
map<T, U, Z extends mapperOpts<T,U>>(arr: Array<T>, opts: Z): Z extends { cb: MapperFn<T,U> } ? U[]: T[];
}
const obj: mapper = {
map: (arr, { cb }) => {
if (!cb) return arr;
return arr.map(cb);
}
}
const arr: number[] =[1,2,3];
const result = obj.map(arr, {cb: (element) => element.toString() }); // should be typed as `string[]`
const result2 = obj.map(arr, { cb: (element) => element+1 }); // should be typed as `number[]`
const result3 = obj.map(arr, {}); // should be types as `number[]`
但是,我收到错误消息:
Type '<T, U, Z extends mapperOpts<T, U>>(arr: T[], { cb }: Z) => T[] | U[]' is not assignable to type '<T, U, Z extends mapperOpts<T, U>>(arr: T[], opts: Z) => Z extends { cb: MapperFn<T, U>; } ? U[] : T[]'.
Type 'T[] | U[]' is not assignable to type 'Z extends { cb: MapperFn<T, U>; } ? U[] : T[]'.
Type 'T[]' is not assignable to type 'Z extends { cb: MapperFn<T, U>; } ? U[] : T[]'.
请注意,result
和 result2
标记为 unknown[]
,这可能意味着回调函数的参数类型推断无法正常工作。
我错过了什么?
具体如何 generic type argument inference works doesn't seem to be particularly well documented, aside from the now obsolete TypeScript Language Specification。
但一般来说,当编译器看到对调用签名为 func<T, U, V>(x: F<T, U>, y: G<T, V>): H<U, V>;
且形式为 c = func(a, b)
的函数的调用时,它需要尝试推断类型参数 T
、U
和 V
来自值 [=17=]、b
和 c
的类型。要推断 U
,编译器需要检查 a
和 c
的类型,因为参数 x
和 return 类型都依赖于 U
。因此 x
和 return 类型是 T
的潜在 推理站点 。另一方面,尝试使用 b
来推断有关 U
的任何内容是没有希望的,因为 y
参数的类型根本不引用 U
.也就是说,y
不是 U
.
的推理站点
并非所有推理站点都得到平等对待,有些推理站点比其他站点更难推理。 Return 类型通常是较差的推理站点,因为编译器通常不知道预期的 return 类型......如果你写 const c = func(a, b);
你是在要求编译器推断 c
,所以 func()
的 return 类型未知。您只能在 return 类型的情况下使用,例如 c
已经是已知类型,例如 const c: SomeType = func(a, b);
.
涉及类型参数的类型函数越复杂,推理站点的用处就越小。对于像 f<T>(x: T): void
这样的东西,对 f(a)
的调用很容易将 T
推断为 a
的类型。但是对于像 g<T>(x: T[keyof T]): void
这样的东西,几乎不可能从 g(a)
推断出 T
。在前一种情况下,您是从相同类型的值推断 T
。简单。在后一种情况下,从该类型属性的联合值推断 T
。你甚至不知道如何开始。其他类型的函数往往介于这两个极端之间。如果您 运行 遇到问题,最好的办法是简化推理站点中的类型函数。
最后,有些地方根本不作为推理站点。像 h<T, U extends T>(u: U): void
这样的函数签名没有 T
的推理站点。 Generic constraints 在推断类型参数时不参考。也许您可能希望编译器从传递给 h()
的内容中推断出 U
,然后从 U
中推断出 T
,但事实并非如此。 T
肯定会无法推断,并且会退回到 unknown
.
之类的东西
对于出现此问题的问题,您可以查看 microsoft/TypeScript#38183, microsoft/TypeScript#31529,可能还有许多其他问题(我会搜索“推理站点”)。
综上所述,我对您的 map()
方法的建议是:
interface Mapper {
map<T, Z extends MapperOpts<T, any>>(arr: Array<T>, opts: Z):
Z extends { cb: MapperFn<T, infer U> } ? U[] : T[];
}
之前的版本U
没有合理的推理站点。相反,我们只会从 arr
和 opts
推断出 T
和 Z
。这很可能会成功。由此,我们可以使用 Z
通过显式 conditional type inference.
提取 U
让我们看看它是如何工作的:
const result = obj.map(arr, { cb: (element) => element.toString() }); // string[]
const result2 = obj.map(arr, { cb: (element) => element + 1 }); // number[]
const result3 = obj.map(arr, {}); // number[]
看起来不错!
尝试创建一个简单的实用程序:
- return 按原样在给定数组中
- 或根据给定的可选参数进行转换。 这是代码:
type MapperFn<T, U> = (val: T) => U;
interface mapperOpts<T,U> {
cb?: MapperFn<T,U>
}
interface mapper {
map<T, U, Z extends mapperOpts<T,U>>(arr: Array<T>, opts: Z): Z extends { cb: MapperFn<T,U> } ? U[]: T[];
}
const obj: mapper = {
map: (arr, { cb }) => {
if (!cb) return arr;
return arr.map(cb);
}
}
const arr: number[] =[1,2,3];
const result = obj.map(arr, {cb: (element) => element.toString() }); // should be typed as `string[]`
const result2 = obj.map(arr, { cb: (element) => element+1 }); // should be typed as `number[]`
const result3 = obj.map(arr, {}); // should be types as `number[]`
但是,我收到错误消息:
Type '<T, U, Z extends mapperOpts<T, U>>(arr: T[], { cb }: Z) => T[] | U[]' is not assignable to type '<T, U, Z extends mapperOpts<T, U>>(arr: T[], opts: Z) => Z extends { cb: MapperFn<T, U>; } ? U[] : T[]'.
Type 'T[] | U[]' is not assignable to type 'Z extends { cb: MapperFn<T, U>; } ? U[] : T[]'.
Type 'T[]' is not assignable to type 'Z extends { cb: MapperFn<T, U>; } ? U[] : T[]'.
请注意,result
和 result2
标记为 unknown[]
,这可能意味着回调函数的参数类型推断无法正常工作。
我错过了什么?
具体如何 generic type argument inference works doesn't seem to be particularly well documented, aside from the now obsolete TypeScript Language Specification。
但一般来说,当编译器看到对调用签名为 func<T, U, V>(x: F<T, U>, y: G<T, V>): H<U, V>;
且形式为 c = func(a, b)
的函数的调用时,它需要尝试推断类型参数 T
、U
和 V
来自值 [=17=]、b
和 c
的类型。要推断 U
,编译器需要检查 a
和 c
的类型,因为参数 x
和 return 类型都依赖于 U
。因此 x
和 return 类型是 T
的潜在 推理站点 。另一方面,尝试使用 b
来推断有关 U
的任何内容是没有希望的,因为 y
参数的类型根本不引用 U
.也就是说,y
不是 U
.
并非所有推理站点都得到平等对待,有些推理站点比其他站点更难推理。 Return 类型通常是较差的推理站点,因为编译器通常不知道预期的 return 类型......如果你写 const c = func(a, b);
你是在要求编译器推断 c
,所以 func()
的 return 类型未知。您只能在 return 类型的情况下使用,例如 c
已经是已知类型,例如 const c: SomeType = func(a, b);
.
涉及类型参数的类型函数越复杂,推理站点的用处就越小。对于像 f<T>(x: T): void
这样的东西,对 f(a)
的调用很容易将 T
推断为 a
的类型。但是对于像 g<T>(x: T[keyof T]): void
这样的东西,几乎不可能从 g(a)
推断出 T
。在前一种情况下,您是从相同类型的值推断 T
。简单。在后一种情况下,从该类型属性的联合值推断 T
。你甚至不知道如何开始。其他类型的函数往往介于这两个极端之间。如果您 运行 遇到问题,最好的办法是简化推理站点中的类型函数。
最后,有些地方根本不作为推理站点。像 h<T, U extends T>(u: U): void
这样的函数签名没有 T
的推理站点。 Generic constraints 在推断类型参数时不参考。也许您可能希望编译器从传递给 h()
的内容中推断出 U
,然后从 U
中推断出 T
,但事实并非如此。 T
肯定会无法推断,并且会退回到 unknown
.
对于出现此问题的问题,您可以查看 microsoft/TypeScript#38183, microsoft/TypeScript#31529,可能还有许多其他问题(我会搜索“推理站点”)。
综上所述,我对您的 map()
方法的建议是:
interface Mapper {
map<T, Z extends MapperOpts<T, any>>(arr: Array<T>, opts: Z):
Z extends { cb: MapperFn<T, infer U> } ? U[] : T[];
}
之前的版本U
没有合理的推理站点。相反,我们只会从 arr
和 opts
推断出 T
和 Z
。这很可能会成功。由此,我们可以使用 Z
通过显式 conditional type inference.
U
让我们看看它是如何工作的:
const result = obj.map(arr, { cb: (element) => element.toString() }); // string[]
const result2 = obj.map(arr, { cb: (element) => element + 1 }); // number[]
const result3 = obj.map(arr, {}); // number[]
看起来不错!