Подтвердить что ты не робот

Получить типы аргументов для конструктора функций/классов

Я пытаюсь сделать что-то, что я не уверен в TypeScript: вывод типов аргументов/возвращаемых типов из функции.

Например

function foo(a: string, b: number) {
  return '${a}, ${b}';
}

type typeA = <insert magic here> foo; // Somehow, typeA should be string;
type typeB = <insert magic here> foo; // Somehow, typeB should be number;

Моим вариантом использования является попытка создать объект конфигурации, содержащий конструкторы и параметры:

Например:

interface IConfigObject<T> {
    // Need a way to compute type U based off of T.
    TypeConstructor: new(a: U): T;
    constructorOptions: U;
}

// In an ideal world, could infer all of this from TypeConstructor

class fizz {
    constructor(a: number) {}
}

const configObj : IConfigObj = {
    TypeConstructor: fizz;
    constructorOptions: 13; // This should be fine
}

const configObj2 : IConfigObj = {
    TypeConstructor: fizz;
    constructorOptions: 'buzz'; // Should be a type error, since fizz takes in a number
}

Может кто-нибудь мне помочь? Благодарю!

4b9b3361

Ответ 1

С помощью TypeScript 2.8 вы можете использовать новое ключевое слово extends:

type FirstArgument<T> = T extends (arg1: infer U, ...args: any[]) => any ? U : any;
type SecondArgument<T> = T extends (arg1: any, arg2: infer U, ...args: any[]) => any ? U : any;

let arg1: FirstArgument<typeof foo>; // string;
let arg2: SecondArgument<typeof foo>; // number;
let ret: ReturnType<typeof foo>; // string;

Ответ 2

В Typescript 2.8 добавлены условные типы с выводом типа

В Typescript 3.0 добавлено rest-elements-in-tuple-types, так что теперь вы можете получить все аргументы типа Array.

type ArgumentsType<T extends (...args: any[]) => any> = T extends (...args: infer A) => any ? A : never;

type Func = (a: number, b: string) => boolean;
type Args = ArgumentsType<Func> // type Args = [number, string];
type Ret = ReturnType<Func> // type Ret = boolean;

Вы можете использовать это так:

const func = (...args: Args): Ret => { // type the rest parameters and return type
  const [a, b] = args; // spread the arguments into their names
  console.log(a, b); // use the arguments like normal
  return true;
};

// Above is equivalent to:
const func: Func = (a, b) => {
  console.log(a, b);
  return true;
}

Ответ 3

Я сделаю более прямой ответ на случай использования типов аргументов конструктора.

type GetConstructorArgs<T> = T extends new (...args: infer U) => any ? U : never

class Foo {
    constructor(foo: string, bar: number){
        //
    }
}

type FooConstructorArgs = GetConstructorArgs<typeof Foo> // [string, number]

Ответ 4

Typescript теперь имеет встроенную функцию ConstructorParameters, аналогичную встроенной функции Parameters. Убедитесь, что вы передаете тип класса, а не экземпляр:

ConstructorParameters<typeof SomeClass>

Ответ 5

Как насчет этого подхода:

interface IConfigObject<T, U> {
    TypeConstructor: new(a: U) => T;
    constructorOptions: U;
}

class fizz {
    constructor(a: number) {}
}

function createConfig<U, T>(cls: { new (arg: U): T }, arg: U): IConfigObject<T, U> {
    return {
        TypeConstructor: cls,
        constructorOptions: arg
    }
}

const configObj = createConfig(fizz, 3); // ok
const configObj2 = createConfig(fizz, "str"); // error

( { TypeConstructor: new(a: U) => T; constructorOptions: U; } class fizz { constructor(a: number) {} } function createConfig(cls: { new (arg: U): T }, arg: U): IConfigObject { return { TypeConstructor: cls, constructorOptions: arg } } const configObj = createConfig(fizz, 3);//ok const configObj2 = createConfig(fizz, "str");//error rel="nofollow noreferrer">код на детской площадке)


редактировать

Вы можете иметь переменную с индексированным типом:

const configs: { [name: string]: IConfigObject<any, any> } = {
    config1: createConfig(fizz, 3),
    config2: createConfig(fizz, "str"), // error
    config3: createConfig(buzz, "str")
}