通过特定类型的键访问时推断对象的属性类型

时间:2020-03-21 11:50:39

标签: typescript

我有用于提取特定类型键的助手类型:

/**
 * 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']));

TypesScript Playground link

1 个答案:

答案 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;
}

Updated playground

E extends KeysOfType<T, S>(不带[])为我们提供了string[],它表示类型T中的所有键,其值均为类型S(EntityState)。此类型用于为第一个参数类型{[K in E]: S}构造约束。