Я пишу вспомогательную функцию, которая устанавливает значение в объекте на path, вот так
function set<
C extends Collection<any>,
E = C extends Collection<infer U> ? U : never,
K1 extends keyof E = keyof E,
K2 extends keyof E[K1]= keyof E[K1]
V extends E[K1][K2]= E[K1][K2]
>(
collection: C,
entityId: string,
path: [K1, K2],
value: V
)
Это позволяет мне делать следующее
Collection.set(myCommentCollection, someComment.id, ['author', 'name'], 'jack');
Вышеупомянутое отлично работает, если ни атрибуты author, ни name не являются необязательными, и в этом случае компилятор с жалобой на
Argument of type '["author", string]' is not assignable to parameter of type '["author", never]'.
Я хочу добиться того, чтобы компилятор только следил за действительностью пути. т.е. каждый атрибут в пути определен правильно, независимо от того, являются ли они необязательными.
В рамках реализации функции я просто вернусь раньше, если полный путь не определен.
Это возможно? Спасибо
ОБНОВИТЬ
Если stats требуется, а атрибут total является необязательным, приведенное ниже не будет выполнено.
Collection.set(myCollection, someItem.id, ['stats', 'total'], 10)
Ошибка:
Argument of type '[number, "total"]' is not assignable to parameter of type '[never, "total"]'.
Type 'number' is not assignable to type 'never'.
Это упрощенная версия, у меня тоже есть чехлы для 1, 2 и 3 уровней






Проблема в том, что для необязательных полей интерфейса тип поля будет TField|undefined. Для типов объединения, как обычно, у нас есть доступ только к общим полям, поэтому это означает, что ни одно из полей o TField не появится в keyof TField|undefined, поскольку этот TField не будет иметь ничего общего с undefined.
Мы можем использовать предопределенный условный тип, чтобы удалить undefined из типа поля:
function set<
C extends Collection<any>,
E = C extends Collection<infer U> ? U : never,
K1 extends keyof E = keyof E,
V1 = Exclude<E[K1], undefined>,
K2 extends keyof V1= keyof V1,
V2 extends V1[K2] =V1[K2]
>(
collection: C,
entityId: string,
path: [K1, K2],
value: V2
) {}
//Usage
interface Comment extends Entity {
author?: {
name?: string
}
stats : {
total?: number
}
}
export interface Collection<T extends Entity> extends Object {
readonly entities: { [key: string]: T };
readonly ids: EntityId[];
}
set(commentState, comment.id, ['author', 'name'], 'jack'); //OK
set(commentState, comment.id, ['stats', 'total'], 10); //OK
Это работает почти идеально, но не удастся, если только последний фрагмент пути является условным, т.е. приведенный ниже пример завершится ошибкой, если атрибут total является необязательным Collection.set(myCollection, itemId, ['stats', 'total'], 10).
@Tarlen, не могли бы вы опубликовать более полный пример неудачи?
Обновленный пример
@Tarlen Я не могу воспроизвести вашу ошибку, как вы ее описали. Я обновил свой тестовый код.
Ах, это произошло потому, что я пропустил возвращаемый тип функции, и я связывал результаты одного вызова с другим. Добавлен тип возврата, и он работает. Большое спасибо за помощь и интуицию позади
Вам просто это нужно максимум на 2 уровня?