#typescript #typescript-generics
#typescript #typescript-generics
Вопрос:
Я хочу создать объект, используя значения по умолчанию для интерфейса, но в то же время изменяя структуру объекта. Например, для этого интерфейса:
interface IPerson {
name: string
age: number
}
Я хочу создать объект, подобный этому:
const person: IPerson = {
name: {
type: String,
required: true
},
age: {
type: Number,
required: true
}
}
Единственный способ, который я нашел, это добавить тип объекта в name
и age
из
IPerson
интерфейс, вот так:
interface IPerson {
name: string | IProp
age: number | IProp
}
interface IProp {
type: any
required?: boolean
// ...
}
Однако я не хочу менять исходный интерфейс IPerson
. Итак, я был
думая о чем-то подобном:
const person: IProperties<IPerson> = {
// use properties of IPerson to determine which key/value pairs are valid in this object
}
Комментарии:
1. Я почти уверен, что отвечал на это раньше.. но найти его сложно…
Ответ №1:
Вы можете использовать сопоставленный тип:
interface IPerson {
name: string
age: number
}
type IProperties<T> = {
[K in keyof T]: IProp
}
interface IProp {
type: any
required?: boolean
// …
}
const person: IProperties<IPerson> = {
// …
}
Комментарии:
1. Да, это именно то, что нужно! Спасибо!
2. @Johannes возможно, я неправильно понял вопрос, но разве это не было бы полезно для
required
иtype
быть отражением переданного типа actualul ?3. @TitianCernicova-Драгомир Да, вы правы. Это всего лишь пример, на самом деле не имеет значения, что такое
required
иtype
… Мне просто нужен был способ создания объектов, соответствующих ключамIPerson
интерфейса.
Ответ №2:
Вы можете сделать это, используя условные типы и сопоставленные типы.
interface IPerson {
name: string
age: number
ocupation?: string
}
type PropertyType<T> =
[T] extends [string] ? typeof String :
[T] extends [number] ? typeof Number :
[T] extends [Date] ? typeof Date :
[T] extends [Function] ? typeof Function : // add any other types here
new (...a:any[]) => T // default must be a constructor for whatever T is
type IfRequired<T, K extends keyof T, IfYes, IfNo> =
Pick<T, K> extends Required<Pick<T, K>> ? IfYes : IfNo
type PropertyDefinition<T> = {
[P in keyof T]-?: {
type: PropertyType<Exclude<T[P], null | undefined>> // will not work for unions!
required: IfRequired<T, P, true, false>
}
}
let personDefinition: PropertyDefinition<IPerson> ={
age : {
required: true,
type: Number
},
name: {
required: true,
type: String,
},
ocupation: {
required: false,
type: String
}
}
Комментарии:
1. Таким образом, это фактически проверяет, например,
age: number
используетtype: Number
ли отображенный тип?2. Синтаксис TypeScript может быть ужасным, но это более строгий ответ, чем мой.
3. @Johannes да, для
age
единственно допустимого значения{ type: Number, require: true }
любое другое значение было бы ошибкой компилятора. Я согласен с палео, это может быть сложнее для чтения с точки зрения синтаксиса, его ответ тоже хорош, только менее строгий, полностью ваш вызов, который вы используете 🙂4. @TitianCernicova-Dragomir Это может быть другой вопрос, но просто для полноты картины: можно ли также опустить
required
, если для него установлено значение false? Потому чтоundefined
это уже ложное значение.5. @Johannes это должно сработать %
type PropertyDefinition<T> = { [P in keyof T]-?: { type: PropertyType<Exclude<T[P], null | undefined>> } amp; IfRequired<T, P, { required: true }, { required?: false }> }