类型不可分配给条件类型

Mar*_*rek 5 javascript enums typescript conditional-types

我在 Playground 中有一个 TypeScript 代码片段。请查看 TypeScript Playground或此处:

enum MyTypes {
    FIRST = "FIRST",
    SECOND = "SECOND",
    THIRD = "THIRD"
}

type TFirst = {
    type: MyTypes.FIRST
    foo: string
}

type TSecond = {
    type: MyTypes.SECOND
    foo: string
}

type TThird = {
    type: MyTypes.THIRD
    bar: string
}

type TConditionalType<T> =
    T extends MyTypes.FIRST ? TFirst :
    T extends MyTypes.SECOND ? TSecond :
    T extends MyTypes.THIRD ? TThird :
    null

const getMyObjectBasedOnType = <T extends MyTypes>(type: T): TConditionalType<T> | null => {
    switch (type) {
        case MyTypes.FIRST: {
            return {
                type: MyTypes.FIRST,
                foo: 'test'
            }
        }
        default: {
            return null
        }
    }
}

const firstObject = getMyObjectBasedOnType(MyTypes.FIRST)
// firstObject is type of TFirst or null which is okay
if (firstObject) {
    firstObject.foo
}

Run Code Online (Sandbox Code Playgroud)

有一个函数getMyObjectBasedOnType(type: T)根据type参数返回一个条件类型的对象。这似乎有效,因为firstObject最后的类型是TFirst | null。这里一切都清楚了。

我遇到的问题是当我返回对象时,第 31 行提到的函数内部出现 TypeScript 错误。我明白了: Type '{ type: MyTypes.FIRST; foo: string; }' is not assignable to type 'TConditionalType<T>'.我不知道出了什么问题。据我了解,这是一个TFirst应该没问题的对象。为什么我会收到此错误以及正确的修复方法是什么?

Pie*_*rte 3

关于您的问题,它来自于延迟的条件类型。查看打字稿文档:https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types。(搜索即可conditional types are deferred到达页面中的正确位置)。

关于此设计决策进行了一些简短的讨论: https://github.com/Microsoft/TypeScript/issues/29939

最简单的解决方案是使用更宽松的单独实现签名,同时保留对调用者来说更好的条件类型的公共签名:

type TConditionalType<T> =
    T extends MyTypes.FIRST ? TFirst :
    T extends MyTypes.SECOND ? TSecond :
    T extends MyTypes.THIRD ? TThird :
    null

function getMyObjectBasedOnType<T extends MyTypes>(type: T): TConditionalType<T>; 
function getMyObjectBasedOnType(type: MyTypes): TFirst | TSecond | TThird | null {
  switch (type) {
    case MyTypes.FIRST: {
      return {
        type: MyTypes.FIRST,
        foo: "test"
      }; // nothing wrong here
    }
    case MyTypes.SECOND: {
      return {
        type: MyTypes.FIRST,
        foo: "test"
      }; // unfortunately it would work... The implementation is permissive
    }
    default: {
      return null;
    }
  }
}

const firstObject = getMyObjectBasedOnType(MyTypes.FIRST)
if (firstObject) {
    firstObject.foo; // it would work
    firstObject.bar; // it would fail

}
Run Code Online (Sandbox Code Playgroud)

我仍在研究如何使其与箭头函数一起使用。要了解两者之间的区别,您可以参考这里:Proper use of const for Define Functions in JavaScript