【问题标题】:make typescript show type hint for string literal when union with string primitive type当与字符串原始类型联合时,使打字稿显示字符串文字的类型提示
【发布时间】:2021-05-31 10:31:13
【问题描述】:

有没有办法让 typescript 为以下内容显示类型提示?
我正在尝试在输入“test”时显示“test1”或“test2”类型,但我失败了。

type A = "test1" | "test2" | string

// update 1: it does show 'test1' & 'test2' but not in the 'constant' category (e.g. menuItem/spanner/wrench icon ), but in 'string' category (e.g. 'abc' icon )
// <--- doesn't show 'test1' or 'test2' when i am typing 'test'
const a : A = 'test1' 

在示例 1 中,type A 在字符串中更宽,因此我们将 'test1'/'test2' 的类型定义丢失为字符串文字

理想情况下,变量 a 仍应显示 'test1' 和 'test2' 用于自动完成,如示例 2 中的 bvariable。

示例 1:

示例 2:

【问题讨论】:

  • 你可以这样做:type A = 'test1' | 'test2' | Omit&lt;string, 'test1' | 'test2'&gt; 来保留 A 的类型吗?
  • ✨神奇,它有效!请张贴作为答案,所以我可以接受。如果你能给出一个简短的描述它是如何工作的,那就更好了,但没有它仍然很棒。
  • 嗯,接受的答案有效,但根本不是因为它所说的原因; Omit&lt;string, 'test1' | 'test2'&gt; 并不意味着“任何string 除了"test1""test2";如果它意味着什么,它意味着“任何string 没有名为test1test2 的已知属性”,即...奇怪。在microsoft/TypeScript#29729 GitHub 上有一个关于此的官方问题,我已经提交了一个答案来解释这里发生了什么。

标签: typescript


【解决方案1】:

这个问题的标准答案是microsoft/TypeScript#29729microsoft/TypeScript#33471


stringliteral typesstring 的并集,如"test1" | "test2" | string,等价于string。没有A 类型的值不能分配给string,反之亦然。此外,编译器急切地将这些类型简化为string。从类型系统的角度来看,这种行为是绝对正确的,但不幸的是,从 文档 的角度来看,它并没有真正做到你想要的。

理想情况下,您想要一种方法来防止急切减少联合(或者至少让编译器跟踪 IntelliSense 的联合部分);没有官方支持的方法可以做到这一点,但有一些解决方法:


有一个suggestion in microsoft/TypeScript#29729 可以使用类似品牌原语(如this TS FAQ entry 中所述)来实现此效果:

type OtherString = string & {};

这里,OtherString 的类型是 string &amp; {};这恰好等同于string(“空对象类型”{} 匹配除nullundefined 之外的所有值...是的,甚至像string 这样的原语)但编译器并没有简化它,因此它会跟踪文字的联合:

type A = "test1" | "test2" | OtherString;
// type A = "test1" | "test2" | OtherString
// not reduced

这为您提供了您正在寻找的行为和提示:

function foo(a: A) { }
foo("test1"); // okay
foo("test2"); // okay
foo("test3"); // okay

foo("") //
//   ^ hint here, test1 and test2


当然,这只是一种解决方法,some situations 其中OtherString 可能无法按照您想要的方式运行;所以要小心!

Playground link to code

【讨论】:

    【解决方案2】:

    type A = 'test1' | 'test2' | Omit&lt;string, 'test1' | 'test2'&gt;

    类型A可以是:

    • 测试1
    • 测试2
    • 除 test1 和 test2 之外的任何字符串

    因此,'test1' 和 'test2' 可以保留。


    如果你有更多的字符串字面量类型,你可以为它创建一个新类型以避免重复test1test2 ...

    type fixedString = 'test1' | 'test2' | 'test3' ...
    type A = fixedString | Omit<string, fixedString>
    

    【讨论】:

    • 这行得通,但不是因为你说的原因。 The Omit&lt;T, K&gt; utility type 是一个 mapped type,它从对象中删除已知键。 Omit&lt;string, 'test1' | 'test2'&gt; 表示您想要像 string 这样的类型,但它没有名为 test1test2keys。从表面上看,这是没有意义的; string 没有这样的键,它有点像 toUpperCaselength 这样的键。
    • 从概念上讲,您想要the Exclude&lt;T, U&gt; utility type,但Exclude&lt;string, 'test1' | 'test2'&gt; 只是string; TypeScript 中没有 negated types,也无法从 string 中删除一些字符串文字类型。
    • 之所以Omit&lt;string, 'test1' | 'test2'&gt;是因为它类似于制作branded primitive type as mentioned in this FAQ entry,是microsoft/TypeScript#33471和相关问题的主题。像(string &amp; { tag?: any }) 这样的东西和Omit&lt;string, 'test1' | 'test2'&gt; 一样有效,并且不会假装使用否定类型。
    【解决方案3】:

    您的代码示例显示了类型断言(as A),但图片显示了类型注释:: A。我会回避类型断言,仅仅是因为:

    type A = 'test1' | 'test2'
    
    const a = 'foo' as A // no error
    const aa: A = 'foo' // errors correctly
    

    playground


    关于自动完成,我认为它不能与类型断言一起使用,因为您在编写 test 之后编写 as A,并且如您所见,即使联合类型中没有 string,你可以断言任何字符串as A

    如果你改用类型注释,我对自动完成没有任何问题:

    【讨论】:

    • 我对@9​​87654333@ 的问题是A 类型现在只有'string' 类型定义(如果你将鼠标悬停在'A' 上方),'test1' & test2' 字符串字面定义丢失这不是我想要的。我会根据你的建议更新问题以避免混淆。
    • 我认为这就是联合类型的工作方式。 string 比任何字符串字面量都宽,并且每个字符串字面量都可以赋值给 string,所以它被推断为 string。
    • 是的,这很让人头疼。所以我正在寻找替代方法来解决它
    • 那么你想用这种联合类型解决什么问题?每个字符串都可以分配给它……?‍♂️
    • 更新了问题,很抱歉造成混淆。有答案解决所有问题。请检查一下
    【解决方案4】:

    是的,提示正确显示。

    字符串文字类型与联合类型、类型保护和类型别名很好地结合在一起。您可以结合使用这些功能来获得类似枚举的字符串行为。

    type A = "test1" | "test2";
    
    
    function animate(easing: A) {
      if (easing === "test1") {
          console.log("test1");
        } else if (easing === "test2") {
            console.log("test2");
        } else {
          console.log("ignoring your types though.")
      }
    }
    animate("test1");
    animate("test2");
    animate("test3"); // error:Argument of type '"test3"' is not assignable to parameter of type 'A'.

    您可以传递两个允许的字符串中的任何一个,但任何其他字符串都会给出错误。

    像这样:

    【讨论】:

    • 在我的示例中,我包含原始字符串作为我的联合成员,因此在这种情况下应该允许“test3”。最终结果应显示“test1”和“test2”的类型提示,同时允许其他字符串。打字稿目前是否足够聪明(或解决方法)?
    • 我不这么认为,因为在你的例子中 animate 不允许 'test3' 作为参数。但我仍然希望允许其他任意字符串并且不丢失 'test1' 和 'test2' 类型提示。
    • 那个提示不是 OP 所追求的。图片中的提示旁边有abc,这意味着这是在文档中的其他位置找到的文本。如果它正确检测到智能感知,您会在它旁边看到菜单图标。
    猜你喜欢
    • 2018-04-28
    • 2019-02-22
    • 2020-06-07
    • 1970-01-01
    • 2021-07-02
    • 2021-08-01
    • 2018-12-07
    • 2023-02-03
    相关资源
    最近更新 更多