0

I want to create an object using default values for an interface, but at the same time changing the structure of the object. For example, for this interface:

interface IPerson { name: string age: number } 

I want to create an object like this:

const person: IPerson = { name: { type: String, required: true }, age: { type: Number, required: true } } 

The only way I found is to add the type for the object to name and age of the IPerson interface, like so:

interface IPerson { name: string | IProp age: number | IProp } interface IProp { type: any required?: boolean // ... } 

I do not want to change the original interface IPerson though. So I was thinking of something like this:

const person: IProperties<IPerson> = { // use properties of IPerson to determine which key/value pairs are valid in this object } 
1
  • 1
    I am pretty sure I have answered this before .. but finding it is challenging... Commented Apr 8, 2019 at 8:27

2 Answers 2

2

You can use a mapped type:

interface IPerson { name: string age: number } type IProperties<T> = { [K in keyof T]: IProp } interface IProp { type: any required?: boolean // … } const person: IProperties<IPerson> = { // … } 
Sign up to request clarification or add additional context in comments.

3 Comments

Yes, this is exactly it! Thanks!
@Johannes maybe I misunderstood the question, but wouldn't it be useful for required and type to be a refection of the actualul type passed in ?
@TitianCernicova-Dragomir Yes, you're right. This is just an example, it doesn't really matter what required and type is... I just needed a way to have an objects conforming to the keys of the IPerson interface.
2

You can do this using conditional types and mapped types.

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 } } 

5 Comments

So this actually checks if, e.g. age: number uses type: Number in the mapped type?
The TypeScript syntax can be terrible, but it is a stricter answer than mine.
@Johannes yup, for age the only valid value is { type: Number, require: true } any other value would be a compiler error. I agree with paleo, it may be harder to read as far as syntax goes, his answer is also good just less strict, totally your call which you use :)
@TitianCernicova-Dragomir This might be a different question, but just for completeness sake: Is it also possible to omit required if it's set to false? Because undefined is already a falsy value.
@Johannes this should work %type PropertyDefinition<T> = { [P in keyof T]-?: { type: PropertyType<Exclude<T[P], null | undefined>> } & IfRequired<T, P, { required: true }, { required?: false }> }

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.