我想声明mapper1它的值只能是Type1,并且mapper2它的值只能是Type2。如何在不声明密钥类型的情况下做到这一点?
在 TypeScript 中,我有:
import Bar1 from './bar1'; // Type1
import Bar2 from './bar2'; // Type1
import Bar3 from './bar3'; // Type2
import Bar4 from './bar4'; // Type2
const mapper1 = {
foo1: bar1,
foo2: bar2,
} as const;
const mapper2 = {
foo3: bar3,
foo4: bar4,
} as const;
export type MapperKeys = keyof typeof mapper1 | keyof typeof mapper2;
Run Code Online (Sandbox Code Playgroud)
bar1并且bar2具有相同的类型(Type1)。bar3并且bar4具有相同的类型(Type2)。Type1不同于Type2.
MapperKeysmapper1是和mapper2( )键的并集'foo1' | 'foo2' | 'foo3' | 'foo4'。
const mapper1: Record<string, Type1> = {
foo1: bar1,
foo2: bar2,
} as const;
const mapper2: Record<string, Type2> = {
foo3: bar3,
foo4: bar4,
} as const;
Run Code Online (Sandbox Code Playgroud)
但现在MapperKeys是'string'。我希望它是mapper1和mapper2( 'foo1' | 'foo2' | 'foo3' | 'foo4')键的并集
const mapper1: Record<'foo1' | 'foo2', Type1> = {
foo1: bar1,
foo2: bar2,
} as const;
const mapper2: Record<'foo3' | 'foo4', Type2> = {
foo3: bar3,
foo4: bar4,
} as const;
Run Code Online (Sandbox Code Playgroud)
这可以工作,但不是DRY。
jca*_*alz 15
如果您在变量(如 )上使用类型注释const x: T,或在表达式(如 )上使用类型断言x as T,那么您就是在告诉编译器将变量或值视为该类型。这本质上会丢弃有关编译器可能推断出的任何更具体类型的信息*。的类型x将扩展为T:
const badMapper1: Record<string, Type1> = { foo1: bar1, foo2: bar2 };
const badMapper2 = { foo3: bar3, foo4: bar4 } as Record<string, Type2>;
export type BadMapperKeys = keyof typeof badMapper1 | keyof typeof badMapper2;
// type BadMapperKeys = string
Run Code Online (Sandbox Code Playgroud)
相反,您正在寻找类似于microsoft/TypeScript#7481satisfies中要求的“运算符”之类的内容,并将在 TypeScript 4.9 中引入。这个想法是,像这样的表达式将验证可分配给类型而不将其扩展为。对于该运算符,您可以说类似的话x satisfies TxT T
const mapper1 = { foo1: bar1, foo2: bar2 } satisfies { [key: string]: Type1 }
const mapper2 = { foo3: bar3, foo4: bar4 } satisfies { [key: string]: Type2 }
export type MapperKeys = keyof typeof mapper1 | keyof typeof mapper2;
// type MapperKeys = "foo1" | "foo2" | "foo3" | "foo4"
Run Code Online (Sandbox Code Playgroud)
并完成。
对于 4.9 之前的 TypeScript 版本,您可以编写辅助函数以实现类似的行为。一般形式是这样的:
const satisfies = <T,>() => <U extends T>(u: U) => u;
Run Code Online (Sandbox Code Playgroud)
然后代替x satisfies T你写(比较麻烦)satisfies<T>()(x)。这是有效的,因为satisfies<T>()生成的恒等函数的<U extends T>(u: U)=>u输入类型U被限制为T,并且返回类型是较窄的类型U而不是较宽的类型T。
我们来尝试一下:
const mapper1 = satisfies<Record<string, Type1>>()({ foo1: bar1, foo2: bar2 });
const mapper2 = satisfies<Record<string, Type2>>()({ foo3: bar3, foo4: bar4 });
export type MapperKeys = keyof typeof mapper1 | keyof typeof mapper2;
// type MapperKeys = "foo1" | "foo2" | "foo3" | "foo4"
Run Code Online (Sandbox Code Playgroud)
看起来不错!
在您的情况下,您特别要求指定对象值类型而不是键。如果需要,您可以调整该satisfies函数,以便指定属性值类型T并让编译器仅推断键。像这样的东西:
const satisfiesRecord = <T,>() => <K extends PropertyKey>(rec: Record<K, T>) => rec;
Run Code Online (Sandbox Code Playgroud)
您可以看到它的行为类似:
const mapper1 = satisfiesRecord<Type1>()({ foo1: bar1, foo2: bar2, });
const mapper2 = satisfiesRecord<Type2>()({ foo3: bar3, foo4: bar4, });
export type MapperKeys = keyof typeof mapper1 | keyof typeof mapper2;
// type MapperKeys = "foo1" | "foo2" | "foo3" | "foo4"
Run Code Online (Sandbox Code Playgroud)
*当您将变量注释为联合类型时,这并不完全正确;在这种情况下,编译器将在赋值时缩小变量的类型。但由于Record<string, Type1>不是union类型,所以这不适用于当前情况。
| 归档时间: |
|
| 查看次数: |
6015 次 |
| 最近记录: |