Как передать совместимый тип объединения из одной перегруженной функции в другую в TypeScript? - PullRequest
0 голосов
/ 04 мая 2020

У меня есть две функции, каждая из которых принимает тип объединения в качестве входных данных, и выход которых соответствует типу входных данных. Одна из этих функций вызывает другую. Я полагаю, что должен быть в состоянии представить это, используя перегруженные определения типов, например:

function f1(input: number): number;
function f1(input: string): string;
function f1(input: string | number): string | number {
    return input;
}

function f2(input: number): number;
function f2(input: string): string;
function f2(input: string | number): string | number {
    return f1(input);
}

Однако это приводит к ошибке типа :

No overload matches this call.
  Overload 1 of 2, '(input: number): number', gave the following error.
    Argument of type 'string | number' is not assignable to parameter of type 'number'.
      Type 'string' is not assignable to type 'number'.
  Overload 2 of 2, '(input: string): string', gave the following error.
    Argument of type 'string | number' is not assignable to parameter of type 'string'.
      Type 'number' is not assignable to type 'string'.(2769)

Есть ли другой способ правильно представить, что эти функции сохраняют свой тип ввода? Являются ли перегруженные типы способом к go, или я мог бы как-то заставить эту работу использовать дженерики?

Ответы [ 3 ]

1 голос
/ 04 мая 2020

Добавление перегрузки объединения делает трюк: функция f1 (вход: строка | номер): строка | номер;

function f1(input: number): number;
function f1(input: string): string;
function f1(input: string | number): string | number;
function f1(input: string | number): string | number {
    return input;
}

function f2(input: number): number;
function f2(input: string): string;
function f2(input: string | number): string | number {
    return f1(input);
}

const resultF2Num = f2(12);     // Number
const resultF2Str = f2('12');   // String
const resultF1Num = f1(12);     // Number
const resultF1Str = f1('12');   // String
1 голос
/ 04 мая 2020

TypeScript не автоматически синтезирует подписи вызовов в объединениях из перегруженных подписей вызовов. Существует давняя открытая проблема, microsoft / TypeScript # 14107 , с просьбой об этом, но я не уверен, будет ли она когда-либо реализована. Сейчас вам нужно сделать «что-то еще», которое включает в себя любое количество обходных путей:


Один из обходных путей - добавить подпись вызова объединения, которую вы хотите увидеть:

function f1(input: number): number;
function f1(input: string): string;
function f1(input: string | number): string | number; // added
function f1(input: string | number): string | number {
    return input;
}

function f2(input: number): number;
function f2(input: string): string;
function f2(input: string | number): string | number {
    return f1(input); // okay
}

Другим обходным решением является использование условных типов и обобщений для замены перегрузки одной подписью вызова:

function f1<T extends number | string>(input: T): T extends number ? number : string;
function f1(input: string | number): string | number {
    return input;
}

function f2(input: number): number;
function f2(input: string): string;
function f2(input: string | number): string | number {
    return f1(input); // okay
}

Другим обходным решением является использовать условную логику c при вызове f1(), чтобы ее аргумент всегда был string или number и никогда string | number:

function f2(input: number): number;
function f2(input: string): string;
function f2(input: string | number): string | number {
    return typeof input === "number" ? f1(input) : f1(input); // okay
}

Наконец, окончательный обходной путь использовать утверждение типа :

function f2(input: number): number;
function f2(input: string): string;
function f2(input: string | number): string | number {
    return f1(input as any) as string | number; // make it work
}

Временные решения представляют собой компромисс между сложностью, избыточностью и безопасностью типов. Я уверен, что есть и другие обходные пути; Вам решать, какой из них, если таковые имеются, лучше всего подходит для вашего случая использования. Во всяком случае, надеюсь, что это помогает; удачи!

Детская площадка ссылка на код

0 голосов
/ 04 мая 2020

Я выяснил способ, которым , кажется, работает , определив мои перегрузки как условные типы :


function f1<T extends string | number>(input: T): T extends string ? string : number;
function f1(input: string | number): string | number {
    return input;
}

function f2<T extends string | number>(input: T): T extends string ? string : number
function f2(input: string | number): string | number {
    return f1(input);
}

const testValid1: string = f2('some string');
const testValid2: number = f2(42);
const testInvalid: number = f2('not a number');
...