我有用于提取特定类型键的助手类型:
/**
* From type S extract those keys that assignable to type T
*/
type KeysOfType<S, T> = {
[K in keyof S]: S[K] extends T ? K : never;
}[keyof S];
问题
通过这些键访问时,打字稿无法推断出对象属性为T类型。
问题
是否可以编写这样的函数:当通过这些键访问时,可以接受特定的对象和对象的键,而无需进行类型转换?
interface EntityState<T> {
ids: string[];
entities: { [id: string]: T };
}
function collectIds<T>(obj: T, keys: KeysOfType<T, EntityState<unknown>>[]): string[] {
const ids: string[] = [];
for (const key of keys) {
const state = obj[key];
// TS2339: Property 'ids' does not exist on type 'T[{ [K in keyof T]: T[K] extends EntityState<unknown> ? K : never; }[keyof T]]'.
ids.push(...state.ids); // <-- state not inferred as EntityState<unknown>
}
return ids;
}
const state: {
status: boolean;
first: EntityState<{ value: string; }>;
second: EntityState<{ prop: string; }>;
} = {
status: true,
first: {
ids: ['1'],
entities: {
'1': { value: 'value from first state' }
}
},
second: {
ids: ['id_from_second_state'],
entities: {
'id_from_second_state': { prop: 'prop from second state' }
}
}
};
console.log(collectIds(state, ['first', 'second']));
答案 0 :(得分:0)
就目前而言,您的函数对obj
几乎一无所知,因此无法假定它包含基于第二个参数的所需属性。您做得很好,并为键添加了约束,因此现在您需要为对象添加约束。
它可能以更优雅的方式编写,但仍然:
function collectIds<T extends {[K in E]: S}, E extends KeysOfType<T, S>, S extends EntityState<unknown>>(obj: T, keys: E[]): string[] {
const ids: string[] = [];
for (const key of keys) {
const state = obj[key];
ids.push(...state.ids);
}
return ids;
}
E extends KeysOfType<T, S>
(不带[])为我们提供了string[]
,它表示类型T中的所有键,其值均为类型S(EntityState)。此类型用于为第一个参数类型{[K in E]: S}
构造约束。