Вот несколько типов, которые я использую (упрощено для этого разговора):
export interface NodeId { readonly _nodeId: string }
export interface CellId { readonly _cellId: string }
export type Call = CodeCall | DefinitionCall
export interface CodeCall {
readonly inputs: Array<{
readonly outside: NodeId,
readonly inside: string,
}>,
}
export interface DefinitionCall {
readonly inputs: Array<{
readonly outside: NodeId,
readonly inside: CellId,
}>,
}
Ключевой момент: CodeCall
и DefinitionCall
каждый содержат массив «входов» с перекрывающимися, но разными определениями того, что такое вход.
Вот полезная функция для моего приложения:
export function doesCallUseNode1(call: Call, nodeId: NodeId): boolean {
for (let input of call.inputs) {
if (input.outside === nodeId) {
return true;
}
}
return false;
}
Это работает! Но, черт возьми, было бы неплохо использовать служебную функцию для поиска. Вот сигнатура полезной функции, которая мне нравится:
declare function findWith<T, K extends keyof T>(arr: T[], key: K, value: T[K]): boolean;
Но если я попытаюсь использовать это так,
export function doesCallUseNode2(call: Call, nodeId: NodeId): boolean {
return findWith(call.inputs, "outside", nodeId)
}
Я получаю ошибку! В частности, это ошибка:
Argument of type '{ readonly outside: NodeId; readonly inside: string; }[] | { readonly outside: NodeId; readonly inside: CellId; }[]' is not assignable to parameter of type '{ readonly outside: NodeId; readonly inside: string; }[]'.
Мой анализ: call.inputs
имеет тип {readonly outside: NodeId; readonly inside: string;}[] | {readonly outside: NodeId; readonly inside: CellId;}[]
. findWith
можно вызвать одним из следующих способов:
{readonly outside: NodeId; readonly inside: string;}
, К = 'outside'
{readonly outside: NodeId; readonly inside: CellId;}
, К = 'outside'
Но это не может быть вызвано с T = их объединением. Я думаю, это разумно - TypeScript не знает, что я использую массивы в контексте, в котором это должно иметь смысл.
Я застрял, пытаясь понять, как набрать findWith
, чтобы это работало. Любые идеи? (Заранее благодарю за любую помощь!)
Обновлять: Спасибо Мэтту за полезный ответ ниже. Просто для справки в будущем: я реализовал это следующим образом (используя lodash) ...
export function findWith<T>(arr: Array<T>, key: keyof T, value: T[keyof T]): T | undefined {
return _.find(arr, (o) => _.isEqual(o[key], value))
}
export function hasWith<K extends keyof any, V>(arr: {[key in K]: V}[], key: K, value: V): boolean {
return !!findWith(arr, key, value)
}
Я рад, что hasWith
можно реализовать (желаемым гибким способом), вызвав более строгий findWith
, который содержит больше информации о типе для более строгого использования.
Попробуй это:
declare function findWith<K extends keyof any, V>(arr: {[P in K]: V}[], key: K, value: V): boolean;
Затем вместо того, чтобы пытаться сопоставить T[]
с {readonly outside: NodeId; readonly inside: string;}[] | {readonly outside: NodeId; readonly inside: CellId;}[]
и получить два противоречивых вывода для T
, вам просто нужно, чтобы в массиве был ключ, который вы ищете, что он делает для обоих случаев объединения.
Огромное спасибо! Я подумал о чем-то в этом роде, но пропустил обозначение «[P in K]». Отлично.