我想给予类对象唯一的id类型,即使它们都是字符串。我尝试使用type
,并尝试从具有唯一子类名称的基类派生。
请参见以下示例。type
和extends
都不允许我指示编译器将它们视为唯一类型。我仍然可以将HumanId传递给期望AnimalId的函数,反之亦然。
我知道它们是对象兼容的,从底层JavaScript的Angular 来看,这是完全有意义的。事实上,如果我向AnimalId添加一个unique成员,我会得到预期的错误:
Argument of type 'HumanId' is not assignable to parameter of type 'AnimalId'.
TypeScript是否有一个好的方法来为基本类型创建唯一的类型别名?
// type HumanId = string;
// type AnimalId = string;
class id {
constructor(public value: string) { }
toString(): string { return this.value;}
}
class HumanId extends id { };
class AnimalId extends id { };
function humanTest(id: HumanId): void {
}
function animalTest(id: AnimalId): void {
}
let h: HumanId = new HumanId("1");
let a: AnimalId = new AnimalId("2");
animalTest(h);
6条答案
按热度按时间00jrzges1#
我遇到了这个问题,但我的用例有一个小转折:我想为
number
引入一个独特的类型。考虑一个API,其中您有例如hours: number
、minutes: number
、seconds: number
等。但是你希望类型系统强制所有单位的正确用法。@Evert提到的blog post在这方面是一个很好的资源。这个想法是创建一个交叉点类型,其中包含一些实际上从未使用过的虚拟对象。创建新的唯一类型可以通过泛型帮助器类型来抽象。示例说明:
现在,函数
f(h: Hours, m: Minutes, s: Seconds)
不能被任意的number
调用,但可以确保完全的类型安全。还要注意,该解决方案没有内存/运行时开销。在实践中,这种方法对我来说效果很好,因为这些“不同”类型可以隐式地用于需要
number
的地方。通过例如as Hour
只需要反过来。一个小缺点是像hours += 1
这样的表达式需要替换为hours = (hours + 1) as Hours
。正如在博客文章中所展示的那样,好处通常会超过稍微更明确的语法。旁注:我将泛型类型命名为
Distinct
,因为这个名字对我来说更自然,这是Nim编程语言中调用该特性的方式。twh00eeo2#
正如您提到的,这些类型在结构上是兼容的。使它们唯一的唯一方法是向它们添加唯一的属性。
如果你只想让编译器区分这两者,你可以添加一个没有运行时差异的虚拟唯一成员:
ruyhziif3#
在“TypeScript Deep Dive”GitBook中,有一个更简洁的
enum
用法来区分类型:将这种方法应用到我的原始示例中,我得到了下面的TypeScript,它确实不允许我在“人类”上调用
animalTest
。:)_ = ''
条目(或其他占位符)是必需的。frebpwbc4#
如果您希望这些数据类型在JSON中可序列化,以便在API或数据库结构中使用,则它们需要保持字符串基类型。有两种方法可以做到这一点。
(1)将ID类型实现为字符串文字
由于字符串字面量是字符串,TypeScript通常会做正确的事情。对于你的人类和动物示例,你可以创建下面的字符串文字类型和函数来安全地创建/强制这些类型。
(2)将ID类型实现为枚举
两种实现方式的注意事项
你需要避免像这样输入用作Map的对象:
而是这样做
尽管如此,一些打字工作不会完美。例如,如果使用Object.entries(animalIdToAnimalMap),则键将是字符串而不是AnimalId(AnimalId将是keyof typeof animalIdToAnimalMap的类型)。
希望TypeScript在未来提供互斥的Id,并改进Object. entries等函数的默认类型。在那之前,我希望这仍然有帮助。这些方法确实帮助我避免了许多错误,否则我会以错误的顺序传递id或混淆不同类型的id。
gwo2fgha5#
这里有另一个想法来探索。除了继承,你可以通过泛型定义一个唯一的ID类型:
ogq8wdun6#
有一种方法可以获得以下可用性:
不需要铸造(
as
)!解决方案如下:这个优秀的建议归功于一个Drew Colthorp。请注意,你可以通过泛化然后重新特化来强制通过类型约束:
...但是只要你使用
HumanId
和AnimalId
而不是string
,你就应该很好。相反,“品牌化”(即在对象的类型中包含唯一的伪值)需要强制转换。bluenote 10提出的
Distinct
类型也是如此。顺便说一句,当搜索这个主题时,术语“不透明类型”和“名义类型”很有用。