typescript 基于类ORM模型的枚举类型推理

wpx232ag  于 2023-01-18  发布在  TypeScript
关注(0)|答案(1)|浏览(163)

我有一个enum DataType { NUMBER, STRING }和一个对象。
我希望TypeScript理解example.idnumber(而不是DataType.NUMBER),同样,通过泛型converter()函数,example.name被视为string而不是DataType.STRING
不需要实现该函数,只需根据传递的参数进行type推理即可:

function converter(...): infer type here { ... }

const example = converter({ id: DataType.NUMBER, name: DataType.STRING });

ts.typeof example.id === number
ts.typeof example.name === string

我的目标是用户应该传递一个数据模型(“模式”),函数将返回一个与数据模型兼容的值,类似于ORM。

class Model {
    public get(... columns: string[]) {
        // ...
    }
}

class User extends Model {
    public columns: {
        id: DataType.NUMBER,
        name: DataType.STRING,
        age: DataType.NUMBER
    }
}

const user = new User.get("id", "name")

ts.typeof user === { id: number, name: string }

**编辑#1:**作为补充,在现实中,我可以很容易地得到这样的结果:

function convert<T extends Record<string, DataType>>(schema: T) {
     return {} as ConvertedType = { [K in keyof T]: T[K] extends DataType.NUMBER ? number : T[K] extends DataType.STRING ? string : never };
}

虽然它工作正常,但在更复杂的代码中却无法工作,例如:

interface Schema {
  [k: string]: DataType
}

type SchemaReturn<S extends Schema, C extends keyof S> = {
    [K in C]: S[K] extends DataType.NUMBER ? number : S[K] extends DataType.STRING ? string : never;
};

abstract class Model {
  public schema: Schema = {};
  
  public select<C extends keyof this["schema"]>(... columns: C[]) {
    return {} as SchemaReturn<typeof this["schema"], C>;
  }
}

class User extends Model {
  public schema = {
    id: DataType.NUMBER,
    name: DataType.STRING,
    age: DataType.NUMBER
  }
}

const user = new User;
const data = user.select("id", "name");

data.id; // Must be `number` but got `never`
data.name; // Must be `string` but got `never`
o4tp2gmn

o4tp2gmn1#

我得到的结果非常接近我想要的结果,尽管用const替换了enum,并且在schema属性的类型中产生了某种“奇怪”:

const DataType = {
  NUMBER: 0,
  STRING: 1,
} as const;

interface Schema {
  [k: string]: typeof DataType[keyof typeof DataType];
}

type SchemaReturn<S extends Schema, C extends keyof S> = {
  [K in C] 
    : S[K] extends typeof DataType.NUMBER ? number
    : S[K] extends typeof DataType.STRING ? string
    : never;
};

class User extends Model {
  public schema = {
    // And here are the odd ones:
    id: DataType.NUMBER, // ts.typeof id === 0 (but expected to keeped DataType.NUMBER)
    name: DataType.STRING, // ts.typeof id === 1 (but expected to keeped DataType.STRING)
    age: DataType.NUMBER, // ts.typeof id === 0 (but expected to keeped DataType.NUMBER)
  };
}

// ...
// But here I get exactly the result I would like:
data.id; // ts.typeof data.id === number
data.name; // ts.typeof data.name === string

我愿意接受新的建议,因为我想继续使用enum而不是JS const

相关问题