打字稿部分键映射
Typescript partial key mapping
我正在尝试创建一个通用类型,该类型将使用模板文字映射键。一般来说,我只想从逗号分隔的键列表中创建一个嵌套类型:
type TFoobar = "address" | "year" | "owner.age" | "owner.address.street";
const foobar: DotToType<TFooBar> = {
address: true,
owner: {
age: true,
address: {
street: true
}
}
}
我试过这样实现它:
type DotToType<keys extends string> = {
[Key in keys]: Key extends `${infer Parent}.${infer Leaf}`
? {[key in Parent]: DotToType<Leaf>}
: {[k in Key]: boolean}
}[keys];
虽然它确实有一个正确的类型,但它使属性成为可选的(至少需要一个,但没有任何东西强制它们都存在)
const foobar: DotToType<TFooBar> = {
address: true,
}; //valid, shouldnt be
const foobar: DotToType<TFooBar> = {
owner: {
age: true
},
}; //valid, shouldnt be
const foobar: DotToType<TFooBar> = {
address: true,
year: false,
owner: {
age: true,
address: {
street: true
}
}
}; //valid
我也试过这样做:
type DotToType<keys extends string> = {
[Key in keys as Key extends `${infer Parent}.${infer Leaf}` ? Parent : Key]:
Key extends `${infer Parent}.${infer Leaf}` ? DotToType<Leaf> : boolean
};
虽然它确实强制执行字段,但如果我对同一个对象有多个路径,它就会停止工作。
const foobar: DotToType<"address" | "year" | "owner.address.street"> = {
"address": true,
"year": true,
owner: {
address: {
street: true
}
}
}; // this works fine
const foobar: DotToType<"address" | "year" | "owner.address.street" | "owner.age"> = {
"address": true,
"year": true,
owner: {
address: {
street: true
}
}
}; // this only allows the first `owner.{Leaf}`
您的初始代码生成联合,这就是为什么属性是可选的:
type DotToType<keys extends string> = {
[Key in keys]: Key extends `${infer Parent}.${infer Leaf}`
? {[key in Parent]: DotToType<Leaf>}
: {[k in Key]: boolean}
}[keys];
type MyType = DotToType<TFoobar>
// produces:
type MyType = {
address: boolean;
} | {
year: boolean;
} | {
owner: {
age: boolean;
};
} | {
owner: {
address: {
street: boolean;
};
};
}
您可以使用以下内容:
type TFoobar = "address" | "year" | "owner.age" | "owner.address.street";
type BeforeDot<K extends string> = K extends `${infer Parent}.${infer _}` ? Parent : K;
type ChildrenAfterDot<K extends string, P extends string> = K extends `${P}.${infer $Leaf}` ? $Leaf : never;
type DotToType<keys extends string> = {
[Key in BeforeDot<keys>]: Key extends keys
? boolean
: DotToType<ChildrenAfterDot<keys, Key>>
};
type MyType = DotToType<TFoobar>
// produces:
type MyType = {
address: boolean;
year: boolean;
owner: DotToType<"age" | "address.street">;
}
我正在尝试创建一个通用类型,该类型将使用模板文字映射键。一般来说,我只想从逗号分隔的键列表中创建一个嵌套类型:
type TFoobar = "address" | "year" | "owner.age" | "owner.address.street";
const foobar: DotToType<TFooBar> = {
address: true,
owner: {
age: true,
address: {
street: true
}
}
}
我试过这样实现它:
type DotToType<keys extends string> = {
[Key in keys]: Key extends `${infer Parent}.${infer Leaf}`
? {[key in Parent]: DotToType<Leaf>}
: {[k in Key]: boolean}
}[keys];
虽然它确实有一个正确的类型,但它使属性成为可选的(至少需要一个,但没有任何东西强制它们都存在)
const foobar: DotToType<TFooBar> = {
address: true,
}; //valid, shouldnt be
const foobar: DotToType<TFooBar> = {
owner: {
age: true
},
}; //valid, shouldnt be
const foobar: DotToType<TFooBar> = {
address: true,
year: false,
owner: {
age: true,
address: {
street: true
}
}
}; //valid
我也试过这样做:
type DotToType<keys extends string> = {
[Key in keys as Key extends `${infer Parent}.${infer Leaf}` ? Parent : Key]:
Key extends `${infer Parent}.${infer Leaf}` ? DotToType<Leaf> : boolean
};
虽然它确实强制执行字段,但如果我对同一个对象有多个路径,它就会停止工作。
const foobar: DotToType<"address" | "year" | "owner.address.street"> = {
"address": true,
"year": true,
owner: {
address: {
street: true
}
}
}; // this works fine
const foobar: DotToType<"address" | "year" | "owner.address.street" | "owner.age"> = {
"address": true,
"year": true,
owner: {
address: {
street: true
}
}
}; // this only allows the first `owner.{Leaf}`
您的初始代码生成联合,这就是为什么属性是可选的:
type DotToType<keys extends string> = {
[Key in keys]: Key extends `${infer Parent}.${infer Leaf}`
? {[key in Parent]: DotToType<Leaf>}
: {[k in Key]: boolean}
}[keys];
type MyType = DotToType<TFoobar>
// produces:
type MyType = {
address: boolean;
} | {
year: boolean;
} | {
owner: {
age: boolean;
};
} | {
owner: {
address: {
street: boolean;
};
};
}
您可以使用以下内容:
type TFoobar = "address" | "year" | "owner.age" | "owner.address.street";
type BeforeDot<K extends string> = K extends `${infer Parent}.${infer _}` ? Parent : K;
type ChildrenAfterDot<K extends string, P extends string> = K extends `${P}.${infer $Leaf}` ? $Leaf : never;
type DotToType<keys extends string> = {
[Key in BeforeDot<keys>]: Key extends keys
? boolean
: DotToType<ChildrenAfterDot<keys, Key>>
};
type MyType = DotToType<TFoobar>
// produces:
type MyType = {
address: boolean;
year: boolean;
owner: DotToType<"age" | "address.street">;
}