assume I have this example class, but in reality it has many more properties
class Foo {
name: string
dob: number
constructor(name: string, dob: number) {
this.name = name;
this.dob = dob;
}
get age() {
return new Date().getTime() - this.dob
}
}
Now Typescript is smart and when I instantiate the class it will give me all the right properties:
var classInstance = new Foo('name', new Date().getTime())
classInstance.name // ok
classInstance.dob // ok
classInstance.age // ok
Somewhere in my code the class gets cloned using the Spread Operator, I'm not sure what TS does behind the scene but it is really smart and gives me all the right properties
var classJSON = {...classInstance};
classJSON.name // ok
classJSON.dob // ok
classJSON.age // missing
This is great, however I sometime need to use the type of classJSON
.. The only way I can think to extract it is to do this:
var classJSON = {...new Foo('', 0)}
type ClassJSONType = typeof classJSON;
Is there a way to extract the type straight out of Foo
without needing Javascript to instantiate?
Disclaimer: this post is a mod based on Matt McCutchen's answer. It's a solid solution, pure TS without any JS runtime effect.
type IfEquals<X, Y, T> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? T : never;
type JSONify<T> = Pick<T, {
[P in keyof T]: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T]>;
Above trick excludes all readonly
fields. To further exclude methods, use the following:
type JSONify<T> = Pick<T, {
[P in keyof T]: IfEquals<{ [Q in P]: T[P] extends Function ? never : T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T]>;