如何获得 TypeScript 映射类型的逆?
How can I get the inverse of a TypeScript mapped type?
我正在寻找 TypeScript 映射类型的 "inverse"(其属性严格为字符串,因此为 "inversible")。为了说明我想要的结果,我需要一个通用的 type
type Inverse<M> = ...
能够变身
type MappedType = {
key1: 'value1'
key2: 'value2'
};
进入
/**
* {
* value1: 'key1';
* value2: 'key2';
* }
*/
type MappedTypeInverse = Inverse<MappedType>
我已经尝试了一些方法..但无济于事:
type Inverse<M> = M extends Record<infer O, infer T> ? Record<T, O> : never;
/**
* type MappedTypeInverse = {
* value1: 'key1' | 'key2'
* value2: 'key2' | 'key2'
* }
*/
type MappedTypeInverse = Inverse<MappedType>
type InverseValue<M extends Record<any, any>, V extends M[keyof M]> = V extends M[infer K] ? K : never;
/**
* type MappedTypeInverseValue = unknown // expecting 'key1'
*/
type MappedTypeInverseValue = InverseValue<MappedType, 'value1'>
这可能吗?任何帮助将不胜感激!
以下是完成此操作的方法。它从 借用了一些 "evil magic" 以在过程的中间步骤将并集转换为交集:
type MappedType = {
key1: 'value1';
key2: 'value2';
};
type Intermediate<R extends Record<string, string>> =
R extends Record<infer K, string>
? { [P in K]: { [Q in R[P]]: P; }; }
: never;
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
? I
: never;
type Inverse<R extends Record<string, string>> =
Intermediate<R> extends Record<string, infer T>
? { [K in keyof UnionToIntersection<T>]: UnionToIntersection<T>[K]; }
: never;
type InverseMappedType = Inverse<MappedType>;
// type InverseMappedType = {
// value1: 'key1';
// value2: 'key2';
// }
此方法的另一个好处是,当输入记录包含重复的 属性 值时,它会输出具有适当 属性 值 never
的映射类型:
type MappedType = {
key1: 'value1';
key2: 'value1' | 'value2';
};
type InverseMappedType = Inverse<MappedType>;
// type InverseMappedType = {
// value1: never;
// value2: 'key2';
// }
比我更精通 TypeScript 的人可能知道比这更短的方法来反转映射类型,但这似乎至少完成了工作。
这里有一个瘦alternative(除了Patrick Robert的好方案):
type KeyFromVal<T, V> = {
[K in keyof T]: V extends T[K] ? K : never
}[keyof T];
// we assume the type to be an object literal with string values
// , should also work with number or symbol
type Inverse<M extends Record<string, string>> = {
[K in M[keyof M]]: KeyFromVal<M, K>
};
type MappedType = {
key1: 'value1'
key2: 'value2'
};
type MappedTypeInverse = Inverse<MappedType> // { value1: "key1"; value2: "key2"; }
我正在寻找 TypeScript 映射类型的 "inverse"(其属性严格为字符串,因此为 "inversible")。为了说明我想要的结果,我需要一个通用的 type
type Inverse<M> = ...
能够变身
type MappedType = {
key1: 'value1'
key2: 'value2'
};
进入
/**
* {
* value1: 'key1';
* value2: 'key2';
* }
*/
type MappedTypeInverse = Inverse<MappedType>
我已经尝试了一些方法..但无济于事:
type Inverse<M> = M extends Record<infer O, infer T> ? Record<T, O> : never;
/**
* type MappedTypeInverse = {
* value1: 'key1' | 'key2'
* value2: 'key2' | 'key2'
* }
*/
type MappedTypeInverse = Inverse<MappedType>
type InverseValue<M extends Record<any, any>, V extends M[keyof M]> = V extends M[infer K] ? K : never;
/**
* type MappedTypeInverseValue = unknown // expecting 'key1'
*/
type MappedTypeInverseValue = InverseValue<MappedType, 'value1'>
这可能吗?任何帮助将不胜感激!
以下是完成此操作的方法。它从
type MappedType = {
key1: 'value1';
key2: 'value2';
};
type Intermediate<R extends Record<string, string>> =
R extends Record<infer K, string>
? { [P in K]: { [Q in R[P]]: P; }; }
: never;
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
? I
: never;
type Inverse<R extends Record<string, string>> =
Intermediate<R> extends Record<string, infer T>
? { [K in keyof UnionToIntersection<T>]: UnionToIntersection<T>[K]; }
: never;
type InverseMappedType = Inverse<MappedType>;
// type InverseMappedType = {
// value1: 'key1';
// value2: 'key2';
// }
此方法的另一个好处是,当输入记录包含重复的 属性 值时,它会输出具有适当 属性 值 never
的映射类型:
type MappedType = {
key1: 'value1';
key2: 'value1' | 'value2';
};
type InverseMappedType = Inverse<MappedType>;
// type InverseMappedType = {
// value1: never;
// value2: 'key2';
// }
比我更精通 TypeScript 的人可能知道比这更短的方法来反转映射类型,但这似乎至少完成了工作。
这里有一个瘦alternative(除了Patrick Robert的好方案):
type KeyFromVal<T, V> = {
[K in keyof T]: V extends T[K] ? K : never
}[keyof T];
// we assume the type to be an object literal with string values
// , should also work with number or symbol
type Inverse<M extends Record<string, string>> = {
[K in M[keyof M]]: KeyFromVal<M, K>
};
type MappedType = {
key1: 'value1'
key2: 'value2'
};
type MappedTypeInverse = Inverse<MappedType> // { value1: "key1"; value2: "key2"; }