我无法将 TypeScript 缩小到类型的文字键:
type Area = "local" | "sync";
type AreaMap = {
local: { lorem: number; ipsum: boolean };
sync: { foo: string; bar: symbol };
};
type Foo = {
[Kind in keyof AreaMap]: {
[Prop in keyof AreaMap[Kind]]?: Prop;
};
};
// This is a very gross simplification of what I'm trying to do.
// The type of the values in the objects `local` and `sync` wouldn't be
// the keys themselves, but would depend on them, so this is good enough
// to demonstrate the problem.
const foo: Foo = { local: { lorem: "lorem" }, sync: {} };
function fn<A extends Area, K extends keyof AreaMap[A]>(area: A, key: K) {
// `key` is narrowed down to `string | number | symbol` instead of `lorem | ipsum` or `foo | bar`
// depending on the value of `area` like I expect
thisTakesString(key);
// ^^^
// Argument of type 'string | number | symbol' is not assignable to parameter of type 'string'.
// Type 'number' is not assignable to type 'string'.
foo[area][key] = key;
//^^^^^^^^^^^^
// Type 'K' is not assignable to type 'Foo[A][K]'.
// Type 'keyof AreaMap[A]' is not assignable to type 'Foo[A][K]'.
// Type 'string | number | symbol' is not assignable to type 'Foo[A][K]'.
// Type 'string' is not assignable to type 'Foo[A][K]'.
return { area, key };
}
function thisTakesString(value: string) {}
我需要根据
key
的值将fn
内的"lorem" | "ipsum"
缩小为"foo" | "bar"
或area
,但由于某种原因,key
内fn
的类型是string | number | symbol
。
key
我需要的范围。我过去实际上已经使用过它,但想看看是否有另一种方法,因为我不想添加额外的属性,因为数据将存储在一个非常小的限制的数据库中。
您需要简化您的类型并做一些工作。
type Local = { lorem: number; ipsum: boolean };
type Sync = { foo: string; bar: symbol };
type Foo = {
local?: Local,
sync?: Sync
};
const foo: Foo = {};
function fn<T extends keyof Foo, K extends keyof Foo[T]>(area: T, key: K) {
thisTakesString(key); // expected Argument of type 'string | number | symbol' is not assignable to parameter of type 'string'.
if(foo[area]){
//return foo[area][key]; // error Type 'K' cannot be used to index type 'Local | Sync'
return (foo[area])[key]; // work
}
return (foo[area])[key]; // expected Object is possibly 'undefined'
}
function thisTakesString(value: string) { }