我只是好奇者,有没有一种方法可以区分原子类型以提高TypeScript中的类型安全性?
换句话说,下面有一种方法可以复制行为:
export type Kilos<T> = T & { discriminator: Kilos<T> }; // or something else
export type Pounds<T> = T & { discriminator: Pounds<T> }; // or something else
export interface MetricWeight {
value: Kilos<number>
}
export interface ImperialWeight {
value: Pounds<number>
}
const wm: MetricWeight = { value: 0 as Kilos<number> }
const wi: ImperialWeight = { value: 0 as Pounds<number> }
wm.value = wi.value; // Should give compiler error
wi.value = wi.value * 2; // Shouldn't error, but it's ok if it would, because it would require type casting which asks for additional attention
wm.value = wi.value * 2; // Already errors
const we: MetricWeight = { value: 0 } // Already errors
Run Code Online (Sandbox Code Playgroud)
或者可以将其放入一个容器中的东西:
export type Discriminate<T> = ...
export type Kilos<T> = Discriminate<Kilos<T>>;
export type Pounds<T> = Discriminate<Pounds<T>>;
...
Run Code Online (Sandbox Code Playgroud)
编辑
好的,事实证明有可能使用ZpdDG4gta在这里https://github.com/microsoft/TypeScript/issues/202发现的不可能的类型hack来构建这种类型
但是当前的语言版本有点混乱:
export type Kilos<T> = T & { discriminator: any extends infer O | any ? O : never };
export type Pounds<T> = T & { discriminator: any extends infer O | any ? O : never };
export interface MetricWeight {
value: Kilos<number>
}
export interface ImperialWeight {
value: Pounds<number>
}
const wm: MetricWeight = { value: 0 as Kilos<number> }
const wi: ImperialWeight = { value: 0 as Pounds<number> }
wm.value = wi.value; // Errors, good
wi.value = wi.value * 2; // Errors, but it's +/- ok
wi.value = wi.value * 2 as Pounds<number>; // Shouldn't error, good
wm.value = wi.value * 2; // Errors, good
const we: MetricWeight = { value: 0 } // Errors, good
Run Code Online (Sandbox Code Playgroud)
不幸的是,以下操作无效:
export type Discriminator<T> = T & { discriminator: any extends infer O | any ? O : never }
export type Kilos<T> = Discriminator<T>;
export type Pounds<T> = Discriminator<T>;
export interface MetricWeight {
value: Kilos<number>
}
export interface ImperialWeight {
value: Pounds<number>
}
const wm: MetricWeight = { value: 0 as Kilos<number> }
const wi: ImperialWeight = { value: 0 as Pounds<number> }
wm.value = wi.value; // Doesn't error, this is bad
wi.value = wi.value * 2; // Errors, but it's +/- ok
wi.value = wi.value * 2 as Pounds<number>; // Shouldn't error, good
wm.value = wi.value * 2; // Errors, good
const we: MetricWeight = { value: 0 } // Errors, good
Run Code Online (Sandbox Code Playgroud)
编辑
事实证明,按照@jcalz,还有另一种方法来引入不可能的类型:
export type Kilos<T> = T & { readonly discriminator: unique symbol };
export type Pounds<T> = T & { readonly discriminator: unique symbol };
...
Run Code Online (Sandbox Code Playgroud)
但是仍然缺少一个问题
export type Discriminator<T> = ...
Run Code Online (Sandbox Code Playgroud)
有什么想法使其更清洁吗?由于类型别名使两个类型引用都坚持使用Discriminator ...
只需将其定义为:
const marker = Symbol();
export type Kilos = number & { [marker]?: 'kilos' };
export const Kilos = (value = 0) => value as Kilos;
export type Pounds = number & { [marker]?: 'pounds' };
export const Pounds = (value = 0) => value as Pounds;
Run Code Online (Sandbox Code Playgroud)
然后磅和公斤会自动投射到数字和数字上,但不会相互投射。
let kilos = Kilos(0);
let pounds = Pounds(0);
let wrong: Pounds = Kilos(20); // Error: Type 'Kilos' is not assignable to type 'Pounds'.
kilos = 10; // OK
pounds = 20; // OK
let kilos2 = 20 as Kilos; // OK
let kilos3: Kilos = 30; // OK
pounds = kilos; // Error: Type 'Kilos' is not assignable to type 'Pounds'.
kilos = pounds; // Error: Type 'Pounds' is not assignable to type 'Kilos'.
kilos = Kilos(pounds / 2); // OK
pounds = Pounds(kilos * 2); // OK
kilos = Pounds(pounds / 2); // Error: Type 'Pounds' is not assignable to type 'Kilos'.
kilos = pounds / 2; // OK
pounds = kilos * 2; // OK
Run Code Online (Sandbox Code Playgroud)
如果您想防止从“增强”单位自动转换为“普通”数字,则只需从标记字段中删除可选:
const marker = Symbol();
export type Kilos = number & { [marker]: 'kilos' };
// ------------------------------------^ -?
export const Kilos = (value = 0) => value as Kilos;
// then:
const kilos = Kilos(2); // OK
kilos = 2; // Error
kilos = kilos * 2; // Error
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
182 次 |
| 最近记录: |