【问题标题】:Is it possible to get a string representation of a type name to use as a template literal type?是否可以获得类型名称的字符串表示形式以用作模板文字类型?
【发布时间】:2022-01-12 11:14:33
【问题描述】:

我正在尝试从类型函数返回更简洁的 compile 错误消息,并且想知道是否可以获取类型名称的字符串表示形式?

那么使用this solution(很遗憾这篇文章没有使用锚链接,所以你需要搜索ErrorBrand)来创建错误消息,是否可以执行以下操作:

type ErrorBrand<Err extends string> = Readonly<{
  [k in Err]: void;
}>;

type MyErrorMsgType<T extends string> = ErrorBrand<`The following type caused an error - ${T}`>
type MyType = {field:string};

type GetStringNameOfType<T> = // converts Type to string name of type

type MyTypeName = GetStringNameOfType<MyType> // outputs 'MyType'

type MyTypeError = MyErrorMsgType<MyTypeName>
// output - The following type caused an error - MyType

Playground

【问题讨论】:

  • 您似乎想要microsoft/TypeScript#23689 中要求的无效类型。由于这些不存在,因此只有解决方法。有时我会使用this sort of thing,它使用元组而不是纯字符串。元组尽可能接近轻量级。而且我怀疑 TypeScript 是否会支持类型到字符串文字的转换,因为它会导致与联合到元组类似的混淆和不一致。你真的不想要字符串的子类型,你想要一个错误消息。
  • 如果您愿意,我可以将上述内容写成答案并对其进行扩展(例如,解释为什么如果建议,类型到字符串的文字会被拒绝)。告诉我。
  • @jcalz 那太好了,谢谢!
  • 实际上,我现在第二次猜测自己是否会拒绝 type-to-string-literal 类型。但无论如何我都会写一个答案并指出相关的github问题。

标签: typescript typescript-generics


【解决方案1】:

没有。那不是Template Literal Types。名称中的“文字类型”指的是Typescriptliteral types

除了一般类型的字符串和数字外,我们还可以在类型位置引用具体的字符串和数字。

您引用的“解决方案”是一种 hack,它通过手动编码错误消息来工作,例如ErrorBrand&lt;'No ponies allowed!'&gt;。在我看来,这是对类型系统的滥用,其附加价值值得怀疑,但那是a whole other topic

【讨论】:

  • 我了解模板文字类型的工作原理,谢谢。您的原始(未经编辑)回复没有回答问题。
  • (1) 是的,因为你的问题不是很清楚,而且要求人们去摸索一篇长文章而不是给我们一个摘要就太过分了。请参阅我对您的问题的编辑。 (2) 我的原始回复没有经过编辑;在收到您的评论后我删除了它,这是一个编译时,而不是运行时的事情,(3)我的新答案确实回答了澄清的问题,(4)我赞成你的问题,即使答案是“否”,因为它是有用。
  • 我不是在征求我是否应该使用这种方法的意见。我不完全确定为什么您觉得在 HN 上发表您对此的看法与我提出的问题有任何关系?
  • 对不起,我认为这是编译时间。我的错。
  • 正如我所说,“那是另一个话题”。但是这句话之前的一切都回答了你的问题。我更新了上面的评论以添加另一点...
【解决方案2】:

TypeScript 目前没有将类型名称转换为字符串 literal type 的功能。我找不到对这个确切功能的现有请求(有一些类似的事情,比如被拒绝的microsoft/TypeScript#29944 要求在运行时将类型名称作为字符串文字 value 发出,这绝对是违反规则)所以可能你可以file your own

我不会非常对它被接受持乐观态度,因为类型名称本质上是一个实现细节,虽然它们对文档和编译器消息很有用,但它们并不是那种你真的希望 类型系统 本身可以观察到。 (在将union types 转换为tuple typesthe PR for implementing labeled tuple elements 的请求中考虑this comment,这确保这些标签不会影响类型系统并且仅用于文档/显示)。

假设有一个TypeToString&lt;T&gt; 实用程序类型会产生以下结果:

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&lt;T&gt; 的输出都会泄漏到类型系统中,这些位置通常会保留给实际的文字字符串值。并且因为ABstructurally 相同的,编译器认为它们是相同的类型:

const b: B = a; // okay

类型的名称与类型系统无关。 TypeToString&lt;T&gt; 能够从相同的类型中提取不同的信息是非常奇怪的,并且允许它这样做会对结构类型系统中的可分配性产生一些不幸的影响。


但我不认为您真的希望将这些名称作为字符串文字 types 用作接口中的键名或discriminated union 的判别值,或用作@ 中的类型987654330@,对吧?上面带有whoAmIWha 的示例是该功能的误用

您只希望能够在 messages 中显示类型名称并执行其他字符串操作或插值来格式化这些消息。在我看来,您真正想要的功能是microsoft/TypeScript#23689 中要求的“无效类型”,或者可能是microsoft/TypeScript#40468 中实验性实现的“throw 类型”(关键字是“实验性”;这个特定的 PR 标记为“草稿”,没有理由认为它会被合并到 TypeScript 版本中,至少以这种形式)。

这个想法是有一个像Invalid&lt;T&gt; 这样的类型函数,如果该类型被实例化,则会引发编译器错误,并且错误消息将以某种方式从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&lt;T&gt; 实用程序类型,它具有您正在寻找的确切行为!由于这个 PR 没有被合并,而且可能永远不会被合并,看起来有一个可观察的 TypeToString&lt;T&gt; 字符串文字类型的含义已经被彻底讨论过,甚至没有被真正考虑过。 PR 的存在让我不太相信TypeToString&lt;T&gt; 会被拒绝,但谁知道呢。

无论如何,您可能想给ms/TS#23689 一个?。


但目前,该语言中还没有这样的功能。我们所拥有的只是解决方法。我倾向于使用一个元组类型,就像我在上面传递给Invalid&lt;&gt; 一样,并且只有一个不太可能分配给任何实际值的该类型的属性:

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&lt;T&gt; 问题的,因为如果AB 相同,则["It is", A]["It is", B] 是相同的类型。我仍然不希望这些消息类型出现在类型系统的其他任何地方或运行时,但至少可分配性不会中断。


Playground link to code

【讨论】:

  • 一如既往的精彩解释,感谢您花时间彻底解释这一点!是的,你是对的,我的用例只是类型名称出现在错误消息中,而不是用于其他任何事情
猜你喜欢
  • 2017-04-30
  • 2019-07-07
  • 2012-10-26
  • 1970-01-01
  • 2011-09-17
  • 2010-10-10
  • 1970-01-01
  • 2010-09-15
  • 2021-01-14
相关资源
最近更新 更多