我有一个enum DataType { NUMBER, STRING }
和一个对象const example: Record<string, DataType> = { id: DataType.NUMBER, name: DataType.STRING }
。
我希望TypeScript理解example.id
是number
(而不是DataType.NUMBER
),example.name
通过通用的converter()
函数被视为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`
我得到了一个非常接近我想要的结果,尽管用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
而不是JSconst
。