首页 > 解决方案 > 为什么当映射泛型构造函数的 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 使用示例更新操场并添加FilterUndefinedcreateGeneric函数中。

编辑:2021/06/24 UTC 16:08 更新游乐场以明确目标是从任何构造函数中删除第一个参数类型。

标签: typescript

解决方案


我已经换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"]>

推荐阅读