microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.01k stars 12.48k forks source link

Dynamic Super Types #11967

Open tinganho opened 8 years ago

tinganho commented 8 years ago

Now, that #11929 is PR landed. I would really love to have some syntax to specify an arbitrary super type of T. This can be especially helpful in creating strongly typed ORM database frameworks.

Proposal

If K is constrained to be a key of T i.e K extends keyof T. Then the syntax:

{ ...K: ...T[K] }

Means spread the keys K and values of T in {}.

Example

Here is an example usage of an envisioned ORM framework:

interface IUser {
    id: string;
    name: string;
    email: string;
    createdAt: string;
    updatedAt: string;
    password: string;
    // ...
}

interface Options<T, K extends keyof T> {
     attributes: T[K][];
}

interface Model<IInstance> {
     findOne<K extends keyof IInstance>(options: Options<IInstance, K>): { ...K: ...IInstance[K] };
}

declare namespace DbContext {
   define<T>(): Model<T>;
}

const Users = DbContext.define<IUser>({
   id: { type: DbContext.STRING(50), allowNull: false },
   // ...
});

const user = Users.findOne({
    attributes: ['id', 'email', 'name'],
    where: {
        id: 1,
    }
});

user.id // no error
user.email // no error
user.name // no error

user.password // error 
user.createdAt // error
user.updatedAt // error
HerringtonDarkholme commented 8 years ago

I wonder whether it is doable after #11929 landed. We can use overload + tuple syntax to mock it.

Something like

interface Model<IInstance> {
     findOne<K extends keyof IInstance>(options: [K]): { K: IInstance[K] };
     findOne<K extends keyof IInstance, K1 extends keyof IInstance>(options: [K, K1]): { K: IInstance[K] } & {K1: IInstance[K1]};
   // .... and more
}

If we have variadic kinds in #5453, maybe dynamic super type can be supported directly?

Also, K extends keyof T seems to stand for one single string literal type to me. But in the return type it is used as a variadic kinds. May be ...K extends keyof T is better?

tinganho commented 8 years ago

Also, K extends keyof T seems to stand for one single string literal type to me. But in the return type it is used as a variadic kinds. May be ...K extends keyof T is better?

I stumbled upon this too. Though this problem might be a bit hard to solve perfectly.