打字稿泛型:如何将数组条目映射到对象键

Typescript generics: Howto map array entries to object keys

TLDR:在我的通用函数中,我想要 myFunction(['width', 'left']) 到 return 类型 {width: string, left: string}

长版:

我有一个 Typescript 函数,它有一个字符串数组作为输入,returns 一个以数组的键作为值的对象:

export interface Dictionary<T> {
  [index: string]: T | undefined;
}
var getStyle = function (  
  element: Element,
  propertyNames: readonly string[]
) { 
  let gCS= window.getComputedStyle(element)
  let result: Dictionary<string> = {};
  propertyNames.forEach((prop)=>{
    result[prop]=gCS.getPropertyValue(prop);
  });
  return result;
};    

typescript return 值是 Object/Dictionary,但没有特定属性。

var resultObj = getStyle(document.body, ['width']);
resultObj.width; // should be ok
resultObj.height; // should be not ok

我尝试了很多东西。最棒的是那个:

export type RestrictedDictionary1<T, P extends readonly string[]> = {
    [index in keyof P]?: T | undefined
}
declare function getStyle1<P extends readonly string[]>(  
    element: Element,
    propertyNames: P
): RestrictedDictionary1<string, P>;

var resultObj1 = getStyle1(document.body, ['width']);  // Huh? Why an array
resultObj1.width; // should be ok, but both are unvalid for TS
resultObj1.height; // should be not ok, but both are unvalid for TS

Typescript 现在从中得到了一个数组。我不知道为什么。

最后一次尝试也是 interface,但 [index in P] 部分不起作用

export interface RestrictedDictionary2<T, P extends string[]> {
    [index in P]: T | undefined;  // A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.
}
declare function getStyle2<P extends string[]>(  
    element: Element,
    propertyNames: P
): RestrictedDictionary2<string, P>;

var resultObj2 = getStyle2(document.body, ['width']);
resultObj2.width; // should be ok
resultObj2.height; // should be not ok

您认为语法有点偏离,但您的想法非常接近。如果 Pstring[] 那么 keyof P 只是数组的成员,而不是值。您可以使用 P[number] 来获取数组中值的类型。此外,接口不能包含映射类型([index in P]: ... 语法),只有类型别名可以(type 定义)。还有一个名为 Record 的预定义映射类型,这正是您所需要的,无需定义新类型


var getStyle = function<T extends string> (  
  element: Element,
  propertyNames: readonly T[]
): Record<T, string> { 
  let gCS= window.getComputedStyle(element)
  let result = {} as Record<T, string>;
  propertyNames.forEach((prop)=>{
    result[prop]=gCS.getPropertyValue(prop);
  });
  return result;
};   
var resultObj = getStyle(document.body, ['width']);
resultObj.width; // should be ok
resultObj.height; // err

Play