【问题标题】:Variable return types based on string literal type argument基于字符串文字类型参数的变量返回类型
【发布时间】:2017-02-03 15:14:42
【问题描述】:

我可以根据 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[]' 不可分配给类型 '字符串[]'。

【问题讨论】:

  • test 变量类型应该是string[]|string
  • @AlekseyL。我不同意。如果将"orange" 作为参数传递给doSomething 总是产生string[],那么test 也具有该类型是正确的。您需要使用overloading
  • @JohnWhite doSomething 签名明确指出返回类型为string[]|string,而重载则不是这种情况,您只需根据特定输入指定返回类型
  • @AlekseyL。我想这真的是一个透视问题。但是,"orange" => string[] 是实际代表运行时行为的签名,而不是 "orange" => string | string[]
  • 您可以使用条件类型,请参阅此答案:stackoverflow.com/a/55059318/2684980

标签: typescript


【解决方案1】:

是的,您可以使用重载签名来实现您想要的:

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 orange: string[] = doSomething("orange");
let apple: string = doSomething("apple");

尝试将doSomething("apple") 分配给orange 会产生编译时类型错误:

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

Live demo on TypeScript Playground

需要注意的是,必须始终在函数实现中手动确定使用哪个重载签名,并且函数实现必须support all overload signatures

TypeScript 中的每个重载没有单独的实现,例如 C#。因此,我发现在运行时加强 TypeScript 类型检查是一种很好的做法,例如:

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

【讨论】:

  • 如果这是真的就好了,但除非我弄错了,否则不是。 Typescript 支持具有不同数量参数的签名的重载方法,但不支持具有相同数量的不同类型参数的签名。字符串字面量类型在编译成 JS 时就是简单的字符串,那你的三个doSomething() 函数怎么区分呢?
  • @drewmoore “如何区分你的三个 doSomething() 函数?” -- 在这种情况下,显然需要进行值检查。如果foo 不属于任何受支持的值,则在此处抛出TypeError 也是一个好主意。
  • 您的演示中有编译器错误 :) 这些值检查只能在运行时完成,而不是编译时 - 这是强制执行 TS 类型安全的时候
  • 啊,我该死的。我希望你是对的 :) 这是 TS 2.0 的新功能 - 万岁。我已经 +1 了,但是 OP - 这是 TS >= 2.0.0 的正确答案
  • 我相信这是一个比我接受的答案更好的答案。请将此标记为正确答案。
【解决方案2】:

我有更好的方法。使用泛型,然后将其用作参数的类型(这样您就不需要手动传递泛型,打字稿会自动推断它)。然后您可以使用该类型并选择正确的返回类型。

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');

这样您可以根据自动传递的参数更改函数的返回类型。

【讨论】:

  • Type 'string[]' 不可分配给 type '{ apple: string;橙色:字符串[]; }[P]'。类型 'string[]' 不能分配给类型 'string & string[]'。类型 'string[]' 不可分配给类型 'string'。(2322)typescriptlang.org/play?#code/…
【解决方案3】:

是的,你可以。您只需要使用instanceof 测试您的test 变量。然后打字稿将限制类型。

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[]) {}

更新

您可能只想将方法设为通用。

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[]) {}

更新

实际上,我相信 John White 的答案要好得多。

【讨论】:

  • 泛型就我而言。其他选择对我的用例来说太麻烦了。谢谢
  • 实际上,我相信@john-white 的回答是对您的具体问题的更好回答。请把他的答案标记为正确的。
猜你喜欢
  • 1970-01-01
  • 2021-11-10
  • 1970-01-01
  • 2020-01-07
  • 1970-01-01
  • 2018-05-18
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
相关资源
最近更新 更多