给定字符串常量的元组类型和其他类型的元组类型,请使用字符串键和值类型创建对象类型

时间:2018-07-03 19:15:58

标签: typescript

我一直在考虑如何为daggy之类的类型指定类型,在其中您可以在数组中指定属性名称列表,并创建一些构造函数。

本质上,我认为想要这样的东西:

[A, B] with [X, Y] => { [A]: X, [B]: Y }

其中AB是任意字符串常量类型,而XY是任何类型。例如

['x', 'y'] with [string, number] => { x: string, y: number }

鉴于目标daggy的使用,目标界面可能最终会像

<K0 extends string, K1 extends string>(keys: [K0, K1])
  => <V0, V1>(v0: V0, v1: V1)
    => { [K0]: V0, [K1]: V1 }

我注意到有一点很有效,那就是TS似乎接受[K in NS[number]: ...[K in K0|K1]是有效的。预期效果如下:

function point<
  A extends string,
  B extends string
>(names: [A, B]): { [K in A|B]: number };
function point(names: any[]): { [x: string]: number } {
  return {
    [names[0]]: 0,
    [names[1]]: 0,
  };
}

const p0 = point(['a', 'b']);
// :: { a: number, b: number }

但是,在这种情况下,您不能假设联盟成员的迭代顺序,也无法将这些成员与另一个联盟的成员压缩在一起。

但是,如果我尝试直接使用K extends string类型,就像这样:

function createZip2Object<
  K0 extends string,
  K1 extends string
>(names: [K0, K1]): <V0, V1>(v0: V0, v1: V1) => { [K0]: V0, [K1]: V1 };
function createZip2Object(names: [string, string]): { [x: string]: any } {
  return (...args) => ({
    [names[0]]: args[0],
    [names[1]]: args[1],
  });
}

const point = createZip2Object(['x', 'y']);
const p0 = point(10, 945);

我收到消息A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.'K0' only refers to a type, but is being used as a value here.

即使没有在内部函数中隐藏类型参数的简单示例,也会出现此错误消息:

function boxValue<K extends string, V>(k: K, v: V): { [K]: V } {
  return { [k]: v };
}

const bv = boxValue('v', 42);
// :: {}

这发生在Typescript playground和带有Typescript 2.9.1的VSCode中。

我知道我可以手动指定daggy的构造函数创建者返回的内容的类型,但是我想知道是否存在更通用的方法。

1 个答案:

答案 0 :(得分:2)

您可以使用相交类型和映射类型来创建所需的对象:

function createZip2Object<
    K0 extends string,
    K1 extends string
    >(names: [K0, K1]): <V0, V1>(v0: V0, v1: V1) => { [P in K0]: V0 } & { [P in K1]: V1 };
function createZip2Object(names: [string, string]): { [x: string]: any } {
    return (...args: any[]) => ({
        [names[0]]: args[0],
        [names[1]]: args[1],
    });
}

const point = createZip2Object(['x', 'y']);
const p0 = point(10, 945);