如何在不使用 as 的情况下确保 TypeScript 的 string|string[] 是 string?

Kon*_*ten 3 types typescript

编辑
由于时间的推移,这个问题 已经失去了它的有效性,从评论和答案中似乎可以看出这一点。尽管最初出现,但它不是这个人的骗局。

我有一个通过以下签名工作的翻译功能。

getI18n(id: string) : string { ... }
Run Code Online (Sandbox Code Playgroud)

我注意到输入以下内容有点乏味。

const titles = [
  this.util.getI18n("Donkey"),
  this.util.getI18n("Monkey"),
  ...
  this.util.getI18n("Wonkey")
];
Run Code Online (Sandbox Code Playgroud)

我更喜欢使用这样的东西。

const titles = this.util.getI18n(["Donkey", "Monkey", ..., "Wonkey"]);
Run Code Online (Sandbox Code Playgroud)

所以我引入了一个接受stringstring[]的函数,其签名如下。

getI18n(id: string | string[]) : string | string[] { ... }
Run Code Online (Sandbox Code Playgroud)

这感觉很天才,直到我注意到我必须为愚蠢的 Typescript 解释结果是字符串而不是一些笨拙的巨型字符串或字符串数​​组,最终得到这个(对于非数组翻译)。

someValue.replace("xxx", this.util.getI18n("Donkey") as string);
Run Code Online (Sandbox Code Playgroud)

有没有办法为 TypeScript 解释输出字符串,即使在其他情况下它可能是string[]

Mac*_*ora 5

您可以通过泛型类型属性来实现效果。考虑:

function getI18n<A extends string[] | string>(id: A): A { 
    return id; // example implementation
}

const arr = getI18n(['a', 'b']) // arr is string[]
const str = getI18n('a') // str is string
Run Code Online (Sandbox Code Playgroud)

通用方法的缺点

由于该解决方案适用于 id 函数,问题始于任何实现,TS 抱怨类型,好像唯一的方法是传递参数而不进行任何修改。考虑:

function getI18n<A extends string[] | string>(id: A): A { 
    if (typeof id === 'string') {
        return id.concat('suffix') as A; 
    } else {
        return (id as string[]).map(x => x.concat('suffix')) as A;
    }
}
Run Code Online (Sandbox Code Playgroud)

该解决方案效果很好,但我们需要考虑一些事情:

  • 需要在函数体中使用类型断言
  • 代码没有很好地显示什么输入类型映射到什么输出(重载显示)
  • 输出的类型推断将错误地推断输入的类型

最后一点问题可以在下面的例子中查看:

const str = getI18n('a') // str is type "a"
Run Code Online (Sandbox Code Playgroud)

所以输出类型是“a”,但在示例中实现结果将是一个字符串“asuffix”,所以类型是错误的。

不要那样做,函数应该有一种输入类型

我想在整个主题中添加一件事。我们需要拥有这种多态输入的事实通常是通过常见的 JS 方法继承的,这样的事情在历史上被认为是良好的做法。但在现实中,具有特定输入的功能会更好,它会产生更少的混乱和问题。

创建具有单态类型的函数。

function getI18n(ids: string[]): string[] { 
    return ids.map(id => id + "suffix");
}

const arr = getI18n(['a', 'b'])
const str = getI18n(['a'])
Run Code Online (Sandbox Code Playgroud)

就那么简单。该方法的好处:

  • 类型级别没有条件
  • 价值层面无条件
  • 没有问题 - f 会为这样那样的输入返回什么

将字符串放入数组括号中确实没有成本。


Art*_*hko 5

除了泛型之外,您还可以使用函数重载

function getI18n(id: string[]): string[];
function getI18n(id: string): string;
function getI18n(id: string | string[]): string | string[] {
    if (typeof id === 'string') {
        return id + '_title';
    }
    return id.slice();
}

const title = getI18n('test'); // const title: string
const titles = getI18n(['a', 'b', 'c']); // const titles: string[]
Run Code Online (Sandbox Code Playgroud)

有关此功能的官方文档链接:函数重载