【问题标题】:Type inference using extended type in typescript在打字稿中使用扩展类型进行类型推断
【发布时间】:2019-12-30 14:34:00
【问题描述】:

我有一个工厂,它应该根据提供的枚举键返回一个函数。 这是在 switch case 中完成的,并且那里的所有功能都有不同的有效负载。

我试图实现的目标是为该函数提供这样的类型,它将隐式验证有效负载的所有类型和工厂的返回类型。请参阅下面的示例:

// the list of keys that are used for creation
enum keys {
    key1 = 1,
    key2 = 2
}
// here are interfaces, that tie each key with payload, that should be provided.
// Actually, payload is way more complex (another interfaces) but let's keep it simple
interface Test1 {
    key: keys.key1;
    payload: string;
}
interface Test2 {
    key: keys.key2;
    payload: number;
}

// this is the type, that comes to the function below
type tests = Test1 | Test2;


interface ReturnTypeOfTest1 { returnedObject1: number; }
interface ReturnTypeOfTest2 { returnedObject2: string; }
// this is how I'm configuring what should be returned depending on the key 
// the return type is set to "ResourceModel", which infers a key from a function payload
// and identifies what exactly it should return
interface ModelOfTest {
    [keys.key1]: ReturnTypeOfTest1;
    [keys.key2]: ReturnTypeOfTest2;
}
type ResourceModel<R extends keys> = ModelOfTest[R];


ResourceModel 类型是基于另一个stackoverflow 问题Typescript: Return type of function based on input value (enum) 创建的

使用上面的类型,我可以验证有效负载的类型,但放松返回类型的验证:


function getTest(t: tests): any{
    switch (t.key) {
        case keys.key1: {
            const data = t.payload; // is string
            return {returnedObject1: 123 };
        } case keys.key2: {
            const data = t.payload; // is number
            return {returnedObject2: '123' };

        }
    }
}
getTest({key: keys.key1, payload: '1' }); // ReturnTypeOfTest1 | ReturnTypeOfTest2

或者获得一个正确的返回类型,但是在 switch case 中丢失一个验证:

function getTest<T extends tests>(t: T): ResourceModel<T['key']> {
    switch (t.key) {
        case keys.key1: {
            const d = t.payload; // string | number
            return {returnedObject1: 123}; // also an error here, because it wants me to return both ReturnTypeOfTest1 & ReturnTypeOfTest2
        } case keys.key2: {
            const d = t.payload; // string | number
            return null;

        }
    }
}
 getTest({key: keys.key2, payload: 1 }); // ReturnTypeOfTest2

有没有办法正确输入这个东西?非常感谢您对此提供任何帮助。

【问题讨论】:

  • 让编译器推断返回类型。显式类型化为返回 any 的函数在 99% 的情况下表明存在误解。

标签: typescript typescript-typings


【解决方案1】:

这本质上是 TypeScript 中缺少的功能(参见 microsoft/TypeScript#24085);编译器无法很好地推断未指定的泛型类型参数,例如 TgetTest() 的实现中。当你调用 getTest() 时,T 被指定,然后返回类型被正确计算。但是当你实现 getTest()T 仍然是一个未指定/未解析的类型参数,编译器基本上放弃了。具体来说,它不会尝试根据t.key 的值使用control-flow-based type analysisT extends Test1 | Test2 缩小为Test1Test2。这意味着目前,像这样的泛型类型签名对函数调用者比对函数实现者更有用。

也许有一天会在语言中解决这个问题。目前,有一些解决方法。在这种情况下,我最常使用的是向函数添加一个 overload signature。让调用者看到对他们有用的泛型重载签名,而实现看到对其有用的非泛型联合类型签名:

// call signature, seen by callers
function getTest<T extends tests>(t: T): ResourceModel<T['key']>;
// implementation signature, seen only by implementation
function getTest(t: tests): ResourceModel<keys> {
  switch (t.key) {
    case keys.key1: {
      const d = t.payload; // string 
      const ret: ResourceModel<keys.key1> = { returnedObject1: 123 };
      return ret;
    } case keys.key2: {
      const d = t.payload; // number
      const ret: ResourceModel<keys.key2> = { returnedObject2: '123' };
      return ret;
    }
  }
}

这仍然不是真正的类型安全;如果您切换 return 语句以便 ReturnTypeOfTest1 返回 Test2 并且反之亦然,则上述内容不会抱怨。但这已接近我们在这里可以做到的最好水平,至少目前是这样,因此您在实施时需要小心。

让我们确保调用按预期工作:

getTest({ key: keys.key1, payload: "1" }); // ReturnTypeOfTest1
getTest({ key: keys.key2, payload: 1 }); // ReturnTypeOfTest2

看起来不错。无论如何,我希望这会有所帮助;祝你好运!

Playground Link to code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-12
    • 1970-01-01
    • 2018-06-09
    • 1970-01-01
    • 2020-08-18
    • 1970-01-01
    • 2022-11-23
    • 2020-04-02
    相关资源
    最近更新 更多