通过键返回对象属性的正确类型

问题描述 投票:0回答:1

我有一个带有已知键的 const 对象。每个项目可能有也可能没有特定的属性。 我想要一个函数,给定键,返回值及其适当的类型。

我正在使用 TypeScript 4.6.2。

当然,我可以只输入

data.key?.property
,但我需要以编程方式执行此操作。

这是一个人为的例子来展示我所追求的:

const data = {
 alice: {loves: 3},
 bob: {loves: 'hippos'},
 charlie: {hates: 'toast'},
 denise: {loves: (a:number) => `hello`}
} as const;


function personLoves(name:keyof typeof data) {
    const item = data[name]
    const loves = 'loves' in item ? item.loves : undefined;
    // ^ this line is clearly not right!
    return loves;
}

const aliceLoves = personLoves('alice');
// I want `aliceLoves` to be type number. 

const bobLoves = personLoves('bob');
// I want `bobLoves` to be type string or 'hippo'

const charlieLoves = personLoves('charlie');
// I want `charlieLoves` to be type undefined

const deniseLoves = personLoves('denise');
// I want `deniseLoves` to be type (a:number) => string

游乐场

typescript
1个回答
1
投票

在这种情况下,我建议在 personLoves 上使用

通用签名
来捕捉
name
data
特定 
键的想法。这将允许 TypeScript 根据调用函数时传递的值来跟踪正确的关联返回类型。

与此相结合,我将使用重载签名来捕获您所遇到的两种情况:当人员的数据包含

loves
键时(在这种情况下,我们从该键值的类型推断返回类型) ),而当它不存在时(在这种情况下,返回类型始终为
undefined
)。

// A helper type that we will need to figure out which keys have values with a 'loves' property
type KeysExtending<O, T> = {
    [K in keyof O]: O[K] extends T ? K : never;
}[keyof O];

// Then, the keys of `data` which DO have a 'loves' key are:
type KeysWithLoves = KeysExtending<typeof data, {loves: unknown}> // resolves to: "alice" | "bob" | "denise"

// Then, write one overload case for keys in the above type, and another for everything else.
// Since overloads always try to match to the earliest listed overload, this will first capture the properties that DO have a 'loves' key, and infers the correct return type from that.
// Anything not caught by the first overload must be a name without a 'loves' key in its data, so we give it return type 'undefined'.

function personLoves<T extends KeysWithLoves>(name: T): (typeof data)[T]["loves"];
function personLoves(name: keyof typeof data): undefined;
function personLoves(name: keyof typeof data) {
    const item = data[name]
    const loves = 'loves' in item ? item.loves : undefined;
    return loves;
}

这似乎得到了你想要的结果:


const aliceLoves = personLoves('alice');
// Is the literal number type: 3. 

const bobLoves = personLoves('bob');
// Is the literal string type: 'hippo'

const charlieLoves = personLoves('charlie');
// Is the type: undefined

const deniseLoves = personLoves('denise');
// Is the type: (a:number) => string

打字稿游乐场

© www.soinside.com 2019 - 2024. All rights reserved.