【问题标题】:How to properly use typescript union types with different internals如何正确使用具有不同内部结构的打字稿联合类型
【发布时间】:2023-03-30 06:49:01
【问题描述】:

在下面的示例中,我收到了一个打字稿错误,即使该错误实际上永远不会在代码中发生。我要么将 Category1 或 Category2 传递给函数,因此当返回 ComputedCategory 类型时,它在 computedname 属性下将具有相同的值,但是打字稿无法识别这一点。它认为该值可以来自任一工会。例如,在设置 name 属性时,它会抛出一个错误,基本上是说 { computed: 'category1', name: 'category2' } is not assignable to ComputedCategories' 首先为什么打字稿这样做,为什么它不够聪明,无法意识到这些值是不可能的,第二怎么办我正确地写这个来表达类型?我希望该示例不使用一堆具有特定返回的类型保护,因为这正是我想要避免的。这是一个错误还是我做错了什么?

type Category1 = {
    testName: 'category1';
    name: 'category1'
};
type Category2 = {
    testName: 'category2';
    name: 'category2'
};
type Categories = Category1 | Category2;

type ComputedCategory1 = {
    computed: 'category1'
    name: 'category1'
};
type ComputedCategory2 = {
    computed: 'category2'
    name: 'category2'
};
type ComputedCategories = ComputedCategory1 | ComputedCategory2;


const computeCategory = (category: Categories): ComputedCategories => ({
    computed: category.testName,
    name: category.name
})

// ERROR
Type '{ computed: "category1" | "category2"; name: "category1" | "category2"; }' is not assignable to type 'ComputedCategories'.
  Type '{ computed: "category1" | "category2"; name: "category1" | "category2"; }' is not assignable to type 'ComputedCategory2'.
    Types of property 'computed' are incompatible.
      Type '"category1" | "category2"' is not assignable to type '"category2"'.
        Type '"category1"' is not assignable to type '"category2"'.ts(2322)



【问题讨论】:

    标签: typescript types typescript-generics


    【解决方案1】:

    我认为你最好的选择是函数重载

    function computeCategory(category: Category2): ComputedCategories;
    function computeCategory(category: Category1): ComputedCategories;
    function computeCategory(category: any ): ComputedCategories{ 
     return {
      computed: category.testName,
      name: category.name
    }}
    

    这里是Playground 和官方docs

    【讨论】:

    • 这似乎是正确的答案。这是人们通常使用的功能吗?
    • 实际上这在我的代码库中似乎不起作用,因为它只会导致任何没有类型安全的底部重载。
    • @C.Johnson 它不应该允许any,看看我添加的链接。你能制作一个能反映你的代码库的游乐场吗?
    • 我做了一个例子 jsfiddle 基本上发生了什么。如果您查看页面底部,即使代码将始终运行,animal.makeSound(randomSound) 也会引发 TS 错误。解决这种情况的最佳方法是什么? jsfiddle.net/dpvacyoz
    • 我更新了小提琴来代表一个真正未知的声音,过载无法解决。我基本上需要getAnimalFromSound 来说明它返回的动物与它作为参数接收的声音有关。
    【解决方案2】:

    我不知道你到底想达到什么目的。如果您只想“匹配类型”,那么在type 中,您需要提供正确的类型,例如string,而不是这样的值:

      type Category = {
        testName: string;     //incorrect testName:'category1'
        name: string;
      };
    
      const category: Category = {
        testName: 'category1',
        name: 'category1',
      };
    
      //here you don't need to give any type to function, ts will handle it
      const computeCategory = (obj: Category) => ({
        computed: obj.testName,
        name: obj.testName,
      });
    
      console.log(computeCategory(category));
    

    或者,如果您想具体说明这些值,那么您可以使用这样的枚举(这样您只能使用您在 enum 中写入的值):

      enum Category1 {
        testName = 'category1',
        name = 'category1',
      }
    
      enum Category2 {
        testName = 'category2',
        name = 'category2',
      }
    
      type Object = {
        testName: Category1.testName | Category2.testName;
        name: Category1.name | Category2.name;
      };
    
      const computeCategory = (object: Object) => ({
        computed: object.testName,
        name: object.name,
      });
    
      console.log(computeCategory(Category2));
    

    或者像这样更简单的方法:

      enum Categories {
        testName1 = 'testName1',
        testName2 = 'testName2',
        testName3 = 'testName3',
        name1 = 'name1',
        name2 = 'name2',
        name3 = 'name3',
      }
    
      const computeCategory = (category1: Categories, category2: Categories) => ({
        computed: category1,
        name: category2,
      });
    
      console.log(computeCategory(Categories.testName1, Categories.name1));
    

    【讨论】:

    • 这个答案的问题是您的type ObjectComputedCategories 的类型不同。当允许来自任何一个的值时,它可以具有以下内容。 { testName: 'category1', name: 'category2' }。我想要做的事情没有在这里显示,因为它要复杂得多,但我不明白为什么打字稿允许两种类型的值在同一类型中。我不确定我是否会得到答案,因为这相当复杂并且看起来像一个错误,但我不知道如何解决它。
    猜你喜欢
    • 2020-05-15
    • 2018-11-03
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 2015-08-17
    • 1970-01-01
    • 2018-07-04
    • 2018-12-06
    相关资源
    最近更新 更多