首页 > 解决方案 > 如何在不使用 as 的情况下确保 TypeScript 的 string|string[] 是字符串?

问题描述

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

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

getI18n(id: string) : string { ... }

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

const titles = [
  this.util.getI18n("Donkey"),
  this.util.getI18n("Monkey"),
  ...
  this.util.getI18n("Wonkey")
];

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

const titles = this.util.getI18n(["Donkey", "Monkey", ..., "Wonkey"]);

所以我介绍了一个函数,它接受带有以下签名的字符串字符串[] 。

getI18n(id: string | string[]) : string | string[] { ... }

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

someValue.replace("xxx", this.util.getI18n("Donkey") as string);

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

标签: typescripttypes

解决方案


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

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

通用方法的缺点

由于该解决方案适用于 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;
    }
}

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

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

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

const str = getI18n('a') // str is type "a"

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

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

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

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

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

const arr = getI18n(['a', 'b'])
const str = getI18n(['a'])

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

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

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


推荐阅读