Я пытаюсь определить типы объектов, используя схему. Эта схема представляет собой массив объектов, содержащий все свойства поля.
type Field = {
name: string
kind: 'string' | 'integer' | 'float' | 'bool'
required?: boolean
}
type Schema = { fields: Field[] };
const schema: Schema = {
fields: [
{ name: 'id', kind: 'integer', required: true },
{ name: 'name', kind: 'string', required: true },
{ name: 'debits', kind: 'float' },
]
}
const customer: Record<?> = {
id: 1,
name: 'John Doe',
debits: 50
}
Я пытаюсь получить предполагаемый тип, но безуспешно.
• Вы не можете пометить schema
как Schema
, не выбросив при этом всю важную информацию. Вам нужно позволить TS сделать это и вместо этого написать const schema = ⋯ as const satisfies Schema
. • Я предполагаю, что вас волнует required
сделать свойство необязательным или нет, верно? Если required
отсутствует или false
то это необязательно? • Соответствует ли этот подход вашим потребностям? Если да, то я напишу ответ с объяснением; если нет, то что мне не хватает?
Вы можете определить следующие типы. Не используйте T extends Schema
в type SchemaToRecord<T>
!
Если вы используете T extends Schema
, вы скроете фактическое значение каждого Field
. TypeScript выведет типы, используя тип Field
.
Неиспользование T extends Schema
означает, что вы задерживаете вывод о типе конкретного Field
значения, предусмотренного T
.
type Field = {
kind: 'string' | 'integer' | 'float' | 'bool';
required?: boolean;
};
type KindToType<T> = T extends 'string' ? string:
T extends 'integer' ? number :
T extends 'float' ? number :
T extends 'bool' ? boolean : never;
type GetMandatoryKeys<T> = {
[P in keyof T]: T[P] extends Exclude<T[P], undefined> ? P : never
}[keyof T]
type UndefinedToOptional<T> = Partial<T> & Pick<T, GetMandatoryKeys<T>>
type SchemaToRecord<T> = UndefinedToOptional<{
[K in keyof T]: 'required' extends keyof T[K] ?
('kind' extends keyof T[K] ? KindToType<T[K]['kind']> : never) :
('kind' extends keyof T[K] ? KindToType<T[K]['kind']> | undefined : never)
}>;
Вы можете использовать вышеуказанные типы следующим образом:
const customerSchema = {
id: { kind: 'integer', required: true } as const,
name: { kind: 'string', required: true } as const,
debits: { kind: 'float' } as const,
}
type CustomerSchema = typeof customerSchema;
type CustomerRecord = SchemaToRecord<CustomerSchema>;
// OK
const customer: CustomerRecord = {
id: 1,
name: 'John Doe',
debits: 50
}
// OK
const customer: CustomerRecord = { id: 1, name: 'John Doe', debits: 50 };
// OK
const customer: CustomerRecord = { id: 1, name: 'John Doe' };
// ERROR Type 'string' is not assignable to type 'number'
const customer2: CustomerRecord = { id: 1, name: 'John Doe', debits: '50' };
// ERROR Type 'number' is not assignable to type 'string'
const customer3: CustomerRecord = { id: 1, name: 50 };
Не используйте const customerSchema: Schema = {...
в const customerSchema = {
! вместо этого используйте as const
(в каждом поле).
Если вы используете : Schema
, TypeScript будет определять типы, используя Schema
(тип), а не значение customerSchema
. Это приведет к менее точному выводу типа.
Вы можете увидеть это в действии здесь
Я думаю, нам нужно услышать ОП, означает ли «нет required
» необязательно. Если да, то customer3
следует разрешить, а не отвергнуть. И кстати, ссылка на вашу игровую площадку недействительна.
Думаю, я исправил ссылку, а также исправил другую проблему. Теперь обязательное неопределенное или ложное свойство означает, что свойство является необязательным.
Это работает как шарм... Я реорганизую свой код, чтобы он соответствовал этому примеру... большое спасибо!.
Он не разрешится в
Field
, поскольку вы передали ему буквальный объект без объявленного типа. Его тип буквально является этим объектом. Вам нужно дать некую подсказку или заставить это сделать с помощью утверждения типа.