Нет определенного типа c в TypeScript, который соответствует вашей желаемой структуре. Строка индекса подписи должна применяться к каждому свойству, даже объявленным вручную, таким как id
. То, что вы ищете, это что-то вроде «сигнатуры индекса покоя» или «тип свойства по умолчанию», и в GitHub есть открытое предложение с просьбой: microsoft / TypeScript # 17867 . Некоторое время go была проделана некоторая работа, которая позволила бы это, но она была отложена (см. этот комментарий для получения дополнительной информации). Так что неясно, когда или если это произойдет.
Лучший способ продолжить это было бы, если бы вы могли реорганизовать свой JavaScript, чтобы он не "смешивал" string
- ценный пакет свойств с number
-ценным id
. Например:
type RefactoredT = {
id: number;
props: { [k: string]: string };
}
Здесь id
и props
полностью разделены, и вам не нужно делать какой-либо сложный тип logi c, чтобы выяснить, являются ли ваши свойства number
или string
ценится. Но это потребует кучу изменений в вашем существующем JavaScript и может оказаться невозможным.
С этого момента я буду полагать, что вы не можете реорганизовать свой JavaScript. Но обратите внимание на то, насколько ясно вышеупомянутое по сравнению с беспорядочными вещами, которые появляются:
Один из распространенных обходных путей к отсутствию сигнатур индекса покоя заключается в использовании типа пересечения для обхода ограничение, которое подписные индексы должны применять к каждому свойству:
type IntersectionT = {
id: number;
} & { [k: string]: string };
Это своего рода работы; если задано значение типа IntersectionT
, компилятор видит свойство id
как number
, а любое другое свойство как string
:
function processT(t: IntersectionT) {
t.id.toFixed(); // okay
t.random.toUpperCase(); // okay
t.id = 1; // okay
t.random = "hello"; // okay
}
Но, к сожалению, вы не можете назначить объект, литерал этого типа без компилятора, жалующегося:
t = { id: 1, random: "hello" }; // error!
// Property 'id' is incompatible with index signature.
Вы должны обойти это дальше, делая что-то вроде Object.assign()
:
const propBag: { [k: string]: string } = { random: "" };
t = Object.assign({ id: 1 }, propBag);
Но это раздражает, так как большинство пользователей никогда не подумают синтезировать объект таким окольным путем.
Другой подход заключается в использовании типа generi c для представления вашего тип вместо указанного c типа. Подумайте о написании типа checker , который принимает в качестве входных данных тип кандидата и возвращает что-то совместимое, если и только если этот тип-кандидат соответствует желаемой структуре:
type VerifyT<T> = { id: number } & { [K in keyof T]: K extends "id" ? unknown : string };
Для этого потребуется вспомогательная функция generi c, чтобы вы могли вывести тип generi c T
, например:
const asT = <T extends VerifyT<T>>(t: T) => t;
Теперь компилятор позволит вам использовать литералы объектов и будет проверьте их так, как вы ожидаете:
asT({ id: 1, random: "hello" }); // okay
asT({ id: "hello" }); // error! string is not number
asT({ id: 1, random: 2 }); // error! number is not string
asT({ id: 1, random: "", thing: "", thang: "" }); // okay
Немного сложнее прочитать значение этого типа с неизвестными ключами. Свойство id
хорошо, но другие свойства не будут известны, и вы получите ошибку:
function processT2<T extends VerifyT<T>>(t: T) {
t.id.toFixed(); // okay
t.random.toUpperCase(); // error! random not known to be a property
}
Наконец, вы можете использовать гибридный подход, сочетающий в себе лучшие аспекты пересечения и обобщенные c типы. Используйте тип generi c для создания значений и тип пересечения для их чтения:
function processT3<T extends VerifyT<T>>(t: T): void;
function processT3(t: IntersectionT): void {
t.id.toFixed();
if ("random" in t)
t.random.toUpperCase(); // okay
}
processT3({ id: 1, random: "hello" });
Выше приведена функция перегруженная , где вызывающие см. тип generi c, но реализация видит тип пересечения.
Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код