Jeff Huijsmans
Jeff Huijsmans

Reputation: 1418

Make Typescript infer types from a plain javascript object

I do not know if this is at all possible, so sorry in advance for the potentially stupid question, but here's my problem.

I'm creating a library where a user can provide a database schema like so:

const personSchema: Schema = {
  name: 'person',
  properties: [
    { name: 'firstName', type: 'string', primary: true, required: true },
    { name: 'age', type: 'number', required: false }
  ]
}

Later, when using one of the library's functions, I'd like to be able to do something like this:

db.get(12).firstName; // no error
db.get(12).test; // compile error when in TS or (at least) no intellisense when in JS

And get full intellisense with this.

The library itself will be written in typescript whereas the user can use either javascript or typescript. When using typescript, they will be able to do something like const personCollection = new Collection<Person>();, but in Javascript they'll only provide a Schema object.

Is this at all possible? I know that at least VSCode gives some type hinting while writing javascript, but I've never seen something this "advanced"; inferring types from an object that defines its types using strings.

Upvotes: 0

Views: 747

Answers (1)

255kb - Mockoon
255kb - Mockoon

Reputation: 6974

I think there is no way to infer a type (get() return type) from an Object's properties values which is by definition something existing only at runtime.

What I would do instead is to allow your user to create a specific string literal for his Schema's properties names, something like this:

interface Schema<T> {
  name: string;
  properties: {
    name: T;
    type: "string" | "number";
    primary?: boolean;
    required: boolean;
  }[];
}
type PersonProperties = "firstName" | "lastName" | "age";

type PersonSchema = Schema<PersonProperties>;

const personSchema: PersonSchema = {
  name: "person",
  properties: [
    { name: "firstName", type: "string", primary: true, required: true },
    { name: "age", type: "number", required: false }
  ]
};

const get = function<T>(): T {
  // ...
  return null;
};

const person1 = get<PersonSchema>();
person1.properties[0].name === "firstName"; // name is infered to one of the 3 PersonProperties

Upvotes: 1

Related Questions