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

Типы возвращаемых переменных на основе аргумента типа строкового литерала

Могу ли я иметь тип возвращаемого значения переменной на основе значения аргумента типа строкового литерала в TypeScript 1.8 или 2.0?

type Fruit = "apple" | "orange" 
function doSomething(foo : Fruit) : string | string[] {
    if (foo == "apple") return "hello";
    else return ["hello","world"];
}

var test : string[] = doSomething("orange");

Ошибка: TS2322: Введите строку | string [] 'не присваивается типу 'Строка []'.

4b9b3361

Ответ 1

Да, вы можете. Вам просто нужно проверить переменную test с помощью instanceof. Typescript затем ограничивает тип.

type Fruit = "apple" | "orange" 
function doSomething(foo: Fruit): string | string[] {
    if (foo == "apple") return "hello";
    else return ["hello","world"]
}

// here the type is still inferred as: string | string[]
var test = doSomething("orange");

if (test instanceof String) {
    // TypeScript knows test is type: string
    doSomethingWithString(test);
} else {
    // TypeScript knows test is type: string[]
    doSomethingWithStringArray(test);
}

function doSomethingWithString(input: string) {}
function doSomethingWithStringArray(input: string[]) {}

UPDATE

Вместо этого вы можете просто использовать этот метод.

function doSomething<T>(foo: Fruit): T {
    if (foo == "apple") return "hello";
    else return ["hello","world"]
}

var test1 = doSomething<string>("apple");
var test2 = doSomething<string[]>("orange");

Или другой вариант состоит в том, чтобы инвертировать поток примерно так:

type Fruit = "apple" | "orange" 
function doSomething(foo: Fruit): void {
    if (foo == "apple") 
        doSomthingWithString("hello");
    else 
        doSomethingWithStringArray(["hello","world"]);
}

function doSomethingWithString(input: string) {}
function doSomethingWithStringArray(input: string[]) {}

UPDATE

На самом деле я считаю, что Джон Уайт - гораздо лучший ответ.

Ответ 2

Да, вы можете использовать сигнатуры перегрузки для достижения именно того, что вы хотите:

type Fruit = "apple" | "orange"

function doSomething(foo: "apple"): string;
function doSomething(foo: "orange"): string[];
function doSomething(foo: Fruit): string | string[]
{
    if (foo == "apple") return "hello";
    else return ["hello", "world"];
}

let test1: string[] = doSomething("orange");
let test2: string = doSomething("apple");

Живая демоверсия на TypeScript Playground

Попытка присвоить doSomething("apple") test1 приведет к ошибке типа времени компиляции:

let test1: string[] = doSomething("apple");
 // ^^^^^
 // type string is not assignable to type string[]

Важно отметить, что определение используемой сигнатуры перегрузки всегда должно выполняться вручную при реализации функции, а реализация функции должна поддерживать все сигнатуры перегрузки.

В TypeScript нет отдельных реализаций для каждой перегрузки, как, например, в С#. Поэтому я считаю целесообразным усиливать проверки типов TypeScript во время выполнения, например:

switch (foo) {
    case "apple":
        return "hello";
    case "orange":
        return ["hello", "world"];
    default:
        throw new TypeError("Invalid string value.");
}

Ответ 3

У меня есть лучший подход. используйте обобщенный тип, который затем используется в качестве типа аргумента (поэтому вам не нужно будет передавать обобщенный тип вручную, а машинопись будет выводить его автоматически). Затем вы можете использовать этот тип и выбрать правильный тип возврата.

type Fruit = 'apple' | 'orange';
function doSomething<P extends Fruit>(foo: P): ({ apple: string; orange: string[] })[P] {
  if (foo === 'apple') return 'hello';
  return ['hello','world];
}
const x: string = doSomething('apple');
const y: string[] = doSomething('orange');

Таким образом, вы можете изменить тип возвращаемого значения вашей функции на основе аргумента, переданного автоматически.