基于参数的显式 return 类型

Explicit return type based on arguments

给定一个接受对象数组的函数,并且 returns 一个对象,其键都对应于参数中每个对象的 属性...似乎有应该是获得更明确的 return 类型的方法。

函数-

function objectFromArray(arr: { key: string }[]) {
    let obj: {[k: string]: true} = {};
    for (let {key} of arr) {
        obj[key] = true;
    }
    return obj;
}

如果我运行以下

let myArray = [{key: 'a'}, {key: 'b'}, {key: 'c'}];
let myObj = objectFromArray(myArray);

那么myObj的类型就是

let myObj: {
    [k: string]: true;
}

但我觉得有足够的信息可以让打字稿推断 myObj 的类型实际上是

let myObj: {
    a: true;
    b: true;
    c: true;
}

我的问题是需要在 objectFromArray 函数中做什么才能根据参数派生显式 return 类型。为清楚起见,我不担心 return 类型的值,只担心获取显式键。

如果你想让它起作用,你需要 objectFromArray() 成为 arr 元素的 key 属性 处的 generic function in the type K of the string literal 值:

function objectFromArray<K extends string>(arr: readonly { key: K }[]) {
  let obj = {} as { [P in K]: true }; // have to assert this because it's not true yet
  for (let { key } of arr) {
    obj[key] = true;
  }
  return obj;
}

readonly { key: K }[]中的readonly只是表示我们并不特别要求传入的数组是可变的。这为我们稍后需要的输入提供了更多余地。

return 类型,{ [P in K]: true } 是一个 mapped type giving an object with a true-valued property for each string literal type in K. (You could also use the Record utility type to give the equivalent Record<K, true>). Since {} is not a valid value of this type, you'll need a type assertion 告诉编译器将 obj 视为类型 { [P in K]: true }.


然后当你创建 myArray 时,编译器的默认行为是将其类型扩展为 Array<{key: string}>,完全忘记字符串文字类型 "a""b",以及 "c"。为防止这种情况,您可以使用 const assertion:

let myArray = [{ key: 'a' }, { key: 'b' }, { key: 'c' }] as const;

/* let myArray: readonly [{
    readonly key: "a";
}, {
    readonly key: "b";
}, {
    readonly key: "c";
}] */

注意 const 断言如何使 myArray 成为 readonly 数组,这就是放宽函数输入类型的原因。现在编译器已经足够了解 myArray,让我们调用 objectFromArray():

let myObj = objectFromArray(myArray);
/* let myObj: {
    a: true;
    b: true;
    c: true;
} */

成功!根据需要,myObj 已知具有 abc 类型的 true 属性。

Playground link to code