TypeScript 目前没有将类型名称转换为字符串 literal type 的功能。我找不到对这个确切功能的现有请求(有一些类似的事情,比如被拒绝的microsoft/TypeScript#29944 要求在运行时将类型名称作为字符串文字 value 发出,这绝对是违反规则)所以可能你可以file your own。
我不会非常对它被接受持乐观态度,因为类型名称本质上是一个实现细节,虽然它们对文档和编译器消息很有用,但它们并不是那种你真的希望 类型系统 本身可以观察到。 (在将union types 转换为tuple types 或the PR for implementing labeled tuple elements 的请求中考虑this comment,这确保这些标签不会影响类型系统并且仅用于文档/显示)。
假设有一个TypeToString<T> 实用程序类型会产生以下结果:
interface A { x: string }
interface B { x: string }
type NameOfA = `It is ${TypeToString<A>}`;
// type NameOfA = "It is A"
type NameOfB = `It is ${TypeToString<B>}`;
// type NameOfB = "It is B"
现在人们可以做这样有趣的事情了:
function whoAmI<T>(x: T, name: TypeToString<T>): void { }
const a: A = { x: "" };
whoAmI(a, "A"); // okay
或
interface Wha extends Record<TypeToString<B>, string> { }
const wha: Wha = { B: "hmm" }; // okay?
在这两种情况下,TypeToString<T> 的输出都会泄漏到类型系统中,这些位置通常会保留给实际的文字字符串值。并且因为A 和B 是structurally 相同的,编译器认为它们是相同的类型:
const b: B = a; // okay
类型的名称与类型系统无关。 TypeToString<T> 能够从相同的类型中提取不同的信息是非常奇怪的,并且允许它这样做会对结构类型系统中的可分配性产生一些不幸的影响。
但我不认为您真的希望将这些名称作为字符串文字 types 用作接口中的键名或discriminated union 的判别值,或用作@ 中的类型987654330@,对吧?上面带有whoAmI 和Wha 的示例是该功能的误用。
您只希望能够在 messages 中显示类型名称并执行其他字符串操作或插值来格式化这些消息。在我看来,您真正想要的功能是microsoft/TypeScript#23689 中要求的“无效类型”,或者可能是microsoft/TypeScript#40468 中实验性实现的“throw 类型”(关键字是“实验性”;这个特定的 PR 标记为“草稿”,没有理由认为它会被合并到 TypeScript 版本中,至少以这种形式)。
这个想法是有一个像Invalid<T> 这样的类型函数,如果该类型被实例化,则会引发编译器错误,并且错误消息将以某种方式从T 派生。例如:
function noFunctions<T>(
t: T extends Function ?
Invalid<["Hey,", T, "is a function type. I SAID NO FUNCTIONS!!"]> :
T
): void { }
noFunctions(123); // okay
noFunctions(String); // error!
// -------> ~~~~~~
// Hey, 'StringConstructor' is a function type. I SAID NO FUNCTIONS!!
您不需要将StringConstructor 转换为字符串文字类型"StringConstructor";您只是希望该字符串出现在错误消息中。
有趣的是,microsoft/TypeScript#40468 的 PR 实际上实现了一个 TypeToString<T> 实用程序类型,它具有您正在寻找的确切行为!由于这个 PR 没有被合并,而且可能永远不会被合并,看起来有一个可观察的 TypeToString<T> 字符串文字类型的含义已经被彻底讨论过,甚至没有被真正考虑过。 PR 的存在让我不太相信TypeToString<T> 会被拒绝,但谁知道呢。
无论如何,您可能想给ms/TS#23689 一个?。
但目前,该语言中还没有这样的功能。我们所拥有的只是解决方法。我倾向于使用一个元组类型,就像我在上面传递给Invalid<> 一样,并且只有一个不太可能分配给任何实际值的该类型的属性:
type Invalid<T> = { __errMsg: T };
noFunctions(String); // error!
// -------> ~~~~~~
// Argument of type 'StringConstructor' is not assignable to parameter of
// type 'Invalid<["Hey,", StringConstructor, "is a function. I SAID NO FUNCTIONS!!"]>'.
这不是一个很棒的错误信息,但是
["Hey,", StringConstructor, "is a function. I SAID NO FUNCTIONS!!"]
出现在那里,如果你足够眯眼,至少在某种程度上是人类可读的。元组类型可能是我能想到的最轻量级的语法,它将事物按所需的顺序排列。
注意这是如何避免TypeToString<T> 问题的,因为如果A 和B 相同,则["It is", A] 和["It is", B] 是相同的类型。我仍然不希望这些消息类型出现在类型系统的其他任何地方或运行时,但至少可分配性不会中断。
Playground link to code