强制对象包含枚举的所有键,并且仍然对其值进行类型推断

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

我有一个对象,我想强制它包含枚举的所有键,并且我还希望推断它的值的类型。 所以如果我这样做:

enum RequiredKeys {
    A = 'a',
    B = 'b'
}
const objectThatShouldContainAllRequiredKeys = {
    [RequiredKeys.A]: (id: string) => {}
};
// Argument of type '123' is not assignable to parameter of type 'string'
// Which is great, that's exactly what I want. 
objectThatShouldContainAllRequiredKeys[RequiredKeys.A](123);

但是现在,我尝试强制执行对象键,并且我尝试的每个解决方案都会破坏类型推断。例如:

enum RequiredKeys {
    A = 'a',
    B = 'b'
}
// Property 'b' is missing in type '{ a: (id: string) => void; }' but required in type 'Record<RequiredKeys, Function>'.
// Which is great, that's exactly what I want
const objectThatShouldContainAllRequiredKeys: Record<RequiredKeys, Function> = {
    [RequiredKeys.A]: (id: string) => {}
};
// No error here, which is less great...
objectThatShouldContainAllRequiredKeys[RequiredKeys.A](123);

知道如何才能享受两个世界吗?对象是否强制执行枚举中的所有键并推断对象值? 谢谢!!

typescript enums
3个回答
5
投票

您可以创建身份函数,其类型参数被限制为具有所需的键,因此打字稿将验证传递的对象键并推断其值的类型:

const createWithRequiredKeys = <T extends Record<RequiredKeys, unknown>>(obj: T) => obj;

const withRequiredKeys = createWithRequiredKeys({
    [RequiredKeys.A]: (id: string) => {},
    [RequiredKeys.B]: 'foo',
}); 

// withRequiredKeys is { a: (id: string) => void; b: string; }

withRequiredKeys[RequiredKeys.A](123); // 'number' is not assignable to parameter of type 'string'

游乐场


0
投票

快速解决方案:(第二次更新

enum RequiredKeys {
  A = 'a',
  B = 'b'
}

type Mapped = {
  [RequiredKeys.A]: (id: string) => any,
  [RequiredKeys.B]: (id: number) => any
}

// Property 'b' is missing in type '{ a: (id: string) => void; }' but required in type 'Record<RequiredKeys, Function>'.
// Which is great, that's exactly what I want
const objectThatShouldContainAllRequiredKeys: Mapped = {
  [RequiredKeys.A]: (id: string) => { },
  [RequiredKeys.B]: (id: number) => { }
}
// No error here, which is less great...
const result = objectThatShouldContainAllRequiredKeys[RequiredKeys.A]('ok'); // ok
const result2 = objectThatShouldContainAllRequiredKeys[RequiredKeys.B](1); // ok

const result3 = objectThatShouldContainAllRequiredKeys[RequiredKeys.B]('1'); // error
const result4 = objectThatShouldContainAllRequiredKeys[RequiredKeys.A](2); // error

如果您想要更通用的解决方案,您可以看看这个answer

请记住,如果有更通用的解决方案,您应该为枚举和函数类型创建类型映射


0
投票

@Aleksey L. 的解决方案效果很好!

尽管如此,尝试制定一种通用方法..我想知道是否有人能想出更好的方法来做到这一点:

function createWithRequiredKeys<TKeys extends keyof any>() {
  return <TObject extends Record<TKeys, unknown>>(obj: TObject): TObject => obj;
}
const withRequiredKeys = createWithRequiredKeys<RequiredKeys>()({
  [RequiredKeys.A]: (id: string) => {},
  [RequiredKeys.B]: "foo",
});
© www.soinside.com 2019 - 2024. All rights reserved.