typescript - 为什么当映射泛型构造函数的 ConstructorParameters 时,Typescript 不再确定它是一个元组?
问题描述
鉴于以下
type Constructor<T> = new (...args: any[]) => T;
type MakeFirstUndefined<T extends unknown[]> = {
[I in keyof T]: I extends "0" ? undefined : T[I]
}
type FilterUndefined<T extends unknown[]> = T extends [] ? [] :
T extends [infer H, ...infer R] ?
H extends undefined ? FilterUndefined<R> : [H, ...FilterUndefined<R>] : T
当我将它应用于元组时,如下所示,我能够过滤掉第一个元素
type InputTuple = [number, string, object]
type FirstUndefinedTuple = MakeFirstUndefined<InputTuple> // Type [undefined, string, object]
const outputFirstUndefinedSuccess: FirstUndefinedTuple = [undefined, 'a-string', {}]
const outputFirstUndefinedCorrectlyErrors: FirstUndefinedTuple = [1, 'a-string', {}]
type FirstItemFilteredOut = FilterUndefined<FirstUndefinedTuple> // Type [string, object]
const ouputFilteredSuccess: FilterUndefined<FirstUndefinedTuple> = ['a-string', {}]
const ouputFilteredCorrectlyErrors: FilterUndefined<FirstUndefinedTuple> = ['a-string', {}, 'apple']
然后,当我将相同的概念应用于ConstructorParameters
特定的Constructor
. 有用。
class MagicMonkey {
constructor(public name: string, public tricks: string[]) {
}
}
enum Color {
BLUE,
GREEN,
PURPLE,
RED,
PINK,
}
class FlashyFlamingo {
constructor(public name: string, public colors: Color[], wingSpan: number) {
}
}
function createMagicMonkey(...args: FilterUndefined<MakeFirstUndefined<ConstructorParameters<typeof MagicMonkey>>>) {
return new MagicMonkey('jack', ...args);
}
console.log(createMagicMonkey(['trick 1', 'trick 2']))
console.log(createMagicMonkey(['trick 1', 'trick 2'], 1000)) // Correct Error: Expected 1 arguments, but got 2
// Output:
// MagicMonkey: {
// "name": "jack",
// "tricks": [
// "trick 1",
// "trick 2"
// ]
// }
function createFlashyFlamingo(...args: FilterUndefined<MakeFirstUndefined<ConstructorParameters<typeof FlashyFlamingo>>>) {
return new FlashyFlamingo('', ...args);
}
console.log(createFlashyFlamingo([Color.PINK, Color.PURPLE], 1000))
console.log(createFlashyFlamingo([Color.PINK, Color.PURPLE])) // Correct Error: Expected 2 aguments, but got one
// Output:
// FlashyFlamingo: {
// "name": "",
// "colors": [
// 4,
// 2
// ]
// }
但是,如果我尝试将其泛化为任何Constructor
. 这没用。
function createGeneric<T extends Constructor<any>>(ctor: T, ...args: FilterUndefined<MakeFirstUndefined<ConstructorParameters<T>>>) {
return new ctor('jack', ...args);
}
console.log(createGeneric(MagicMonkey, ['trick 1', 'trick 2']))
// Expected Ouput(same as createMagicMonkey):
// MagicMonkey: {
// "name": "jack",
// "tricks": [
// "trick 1",
// "trick 2"
// ]
// }
console.log(createGeneric(FlashyFlamingo, [Color.PINK, Color.PURPLE], 1000))
// Expected Ouput(same as createFlashyFlamingo):
// FlashyFlamingo: {
// "name": "",
// "colors": [
// 4,
// 2
// ]
// }
我收到错误:其余的“参数必须是数组类型”
作为测试,我删除了该FilterUndefined
部分,即使只是应用MakeFirstUndefined
它仍然说“参数必须是数组类型”。应用后绝对是一个类型MakeFirstUndefined
,然后MakeFirstUndefined
如示例所示。
我看不出它除了元组之外怎么可能是其他任何东西,看到它ConstructorParamters
返回一个元组。映射元组类型现在很流行,对吧?
编辑:2021/06/24 UTC 9:35 使用示例更新操场并添加FilterUndefined
到createGeneric
函数中。
编辑:2021/06/24 UTC 16:08 更新游乐场以明确目标是从任何构造函数中删除第一个参数类型。
解决方案
我已经换MakeFirstUndefined
了新的。很难弄清楚 TS
type MakeFirstUndefined<T extends unknown[]> = {
[I in keyof T]: I extends "0" ? undefined : T[I]
}
产生一个数组。
type TupleWithUndefined = [number, string, undefined, number];
// we need to infer constructor parameters
type Constructor<Args extends string[]> = new <R>(...args: [...Args]) => R;
type MakeFirstUndefined<T extends unknown[]> = T extends [infer _, ...infer Rest] ? [undefined, ...Rest] : T
type FilterUndefined<T extends unknown[]> =
T extends [] ? [] :
T extends [infer H, ...infer R] ?
H extends undefined ? FilterUndefined<R> : [H, ...FilterUndefined<R>] : T
type InputTuple = [number, string, object]
type FilteredTuple = FilterUndefined<MakeFirstUndefined<InputTuple>> // [string, tuple]
const error: FilteredTuple = [1, '2', {}]
const successful: FilteredTuple = ['2', {}]
class MagicMonkey {
constructor(name: string, public tricks: string[]) {
}
}
function create(...args: FilterUndefined<MakeFirstUndefined<ConstructorParameters<typeof MagicMonkey>>>) {
return new MagicMonkey('jack', ...args);
}
function createGeneric<El extends string, Tuple extends El[]>(...args: MakeFirstUndefined<ConstructorParameters<Constructor<Tuple>>> & string[]) {
return new MagicMonkey('jack', args);
}
请让我知道它是否适合您
MagicMonkey
期望第二个参数是,string[]
但我在您的代码中没有发现任何stirng[]
约束。也许值得添加?
更新
type Constructor<Args extends string[]> = new <R>(...args: [...Args]) => R;
class MagicMonkey<Trick extends string, Tricks extends Trick[]> {
constructor(public name: string, public tricks: [...Tricks]) {
}
}
function createGeneric<El extends string, Tuple extends El[]>(...args: ConstructorParameters<Constructor<Tuple>>) {
return new MagicMonkey('jack', args);
}
const result2 = createGeneric('trick1') // MagicMonkey<string, ["trick1"]>
推荐阅读
- c++ - 比较 2 个字符数组 C++
- xamarin.android - Uno Platform - xamarin 无虚拟方法 getTargetState()
- python-3.x - df.copy() 方法不支持的操作数类型
- java - Maven 插件版本范围未正确解析
- python - 如何在 numpy 或 Pytorch 中选择分类分布的百分位数
- python - 添加指定日期范围内的值
- google-apps-script - 项目发布后触发器失败
- r - 尝试下载文件时,R CMD检查在ubuntu中失败,但功能在R中有效
- reactjs - 在玩笑测试中在 Material UI Select 上触发 mousedown 事件时,我总是收到“anchorEl”警告
- podman - 无法使用 podman 推送到本地重新映射的注册表