【问题标题】:Tuple where a succeeding element's type is dependent on the value of a preceding element后一个元素的类型取决于前一个元素的值的元组
【发布时间】:2019-12-27 05:02:34
【问题描述】:

我想我有一个简单的问题,但我也不确定这在 TypeScript 中是否可行。

本质上我想定义一个元组类型,它有两个元素,第二个元素取决于第一个元素的值。

作为一个例子,我想创建一个类型,其中第一个元组元素是接口的键,然后将第二个元组元素绑定到该属性的类型。例如:

interface ExampleI {
  a: number;
  b: string;
}

const one: KeyedTuple<ExampleI> = ["a", 34]; // good
const two: KeyedTuple<ExampleI> = ["a", "not a number"]; // bad
const three: KeyedTuple<ExampleI> = ["b", 47]; // bad

我尝试执行以下操作:

type KeyedTuple<T, K extends keyof T> = [K, T[K]];

这几乎可行,但编译器只考虑K 的类型,而不考虑K 的值,所以第二个元素总是 的类型为number | string

这可能吗?如果有,怎么做?

【问题讨论】:

    标签: typescript generics types tuples


    【解决方案1】:
    const keyedTuple = <T, K extends keyof T>(obj: T, key: K): [T, T[K]] => {
        return [obj, obj[key]]
    }
    
    interface IPerson {
        name: string;
        age: number
    }
    declare const Person: IPerson
    const test = keyedTuple(Person, "name") // [Person, string]
    

    这是实现这一点的一种方法,我更倾向于使用一个函数来实现这一点,而不是记住将冒号或“as”转换为正确的类型。

    除非知道密钥,否则您的代码将无法工作,它不能从变量中推断出来,但可以从函数中推断出来。

    IE 您的代码必须更改为 ObjKeyed&lt;OBJ, KEY&gt; = [Obj, Key]

    编辑:使用类型:

    type KeyedTuple<T, K extends keyof T> = [K, T[K]];
    
    interface ExampleI {
      a: number;
      b: string;
    }
    
    const one: KeyedTuple<ExampleI, "a"> = ["a", 34]; // good
    const two: KeyedTuple<ExampleI, "a"> = ["a", "not a number"]; // bad
    const three: KeyedTuple<ExampleI, "b"> = ["b", 47]; // bad
    

    【讨论】:

    • 虽然这种行为与我所描述的类似,但函数不等同于类型,不能在任何相同的地方使用。
    • 添加编辑希望这有帮助,不可能像我在上面的评论中所说的那样推断变量中的键,所以你必须将它添加到硬编码的类型中
    【解决方案2】:

    从概念上讲,我认为您希望KeyedTuple&lt;T&gt; 成为keyof T 中所有K[K, T[K]] 元组的union。这可以通过mappedlookup 类型来实现,如下所示:

    type KeyedTuple<T> = { [K in keyof T]: [K, T[K]] }[keyof T];
    

    让我们测试一下:

    interface ExampleI {
      a: number;
      b: string;
    }
    
    type KeyedTupleExampleI = KeyedTuple<ExampleI>;
    // type KeyedTupleExampleI = ["a", number] | ["b", string]
    

    它为您提供了您所要求的行为:

    const one: KeyedTuple<ExampleI> = ["a", 34]; // okay
    const two: KeyedTuple<ExampleI> = ["a", "not a number"]; // error
    const three: KeyedTuple<ExampleI> = ["b", 47]; // error
    

    此外,由于赋值在联合类型上充当type guards,编译器将记住变量是哪个键/值对:

    one[1].toFixed(); // okay, remembers one[1] is a number
    

    希望有所帮助;祝你好运!

    Link to code

    【讨论】:

    • 这确实解决了我给出的例子。不幸的是,这是我的问题的简化版本。在更复杂的情况下,我认为创建所有可能类型的联合是不可行的
    • minimal reproducible example 的问题,您认为此地址不会有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-19
    • 2021-07-25
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多