Is there a way to enforce typescript to even more strict typechecking up to per alias checks?
What I want to achieve is to define types, such as:
type kilograms = number;
type kilometers = number;
type kilogramsPerKilometer = number;
And to be sure that I don't put wrongly typed value into a variable such as:
let x: kilograms = 123;
let y: kilometers = 256;
let z: kilogramsPerKilometer = x / y; // Will popup an error here saying that types are incompatible
In which case it would require explicit type cast:
let x: kilograms = 123;
let y: kilometers = 256;
let z: kilogramsPerKilometer = <number>x / <number>y; // Will downcast `kilograms` and `kilometers` types to `number` and then up-cast types to `kilogramsPerKilometer`
There is no nominal typing in TS (yet) https://github.com/Microsoft/TypeScript/issues/202
This is the admitted solution (using discriminating unions):
interface kilograms {
kind: "kilograms";
value: number;
}
interface kilometers {
kind: "kilometers";
value: number;
}
function kilosPerKiloms(x: kilograms, y: kilometers): kilogramsPerKilometer {
return x.value / y.value;
}
const x = { kind: "kilograms", value: 123 };
const y = { kind: "kilometers", value: 256 };
const z = kilosPerKiloms(x, y);
// const z = kilosPerKiloms(y, x); // => error
More informations there:
https://www.typescriptlang.org/docs/handbook/advanced-types.html
https://basarat.gitbooks.io/typescript/content/docs/types/discriminated-unions.html
Somehow I lost track of this question while actually finding an interesting solution. It's prettier, doesn't affect interface and doesn't pollute the IDE's list of suggestions.
export type Miles = number & { readonly '': unique symbol };
export type Kilograms = number & { readonly '': unique symbol };
export type MilesPerKilogram = number & { readonly '': unique symbol };
var miles: Miles = 3; // error
var kilos: Kilograms = 3; // error
var milesPerKilos: MilesPerKilogram = x / y; // error
var x: Miles = 3 as Miles; // ok
var y: Kilograms = 3 as Kilograms; // ok
var u: MilesPerKilogram = x / y as MilesPerKilogram; // ok
miles = kilos; // error, yey!
kilos = miles; // error, yey!
Works on any types
Edit
Be careful as you are not allowed to extract { readonly '': unique symbol }
part into separate type as it would share unique symbol across the types and make them share "nominality" which would beat the purpose of a nominal type:
export type Nominal<T> = T & { readonly '': unique symbol };
export type Miles = Nominal<number>;
export type Kilograms = Nominal<number>;
export type MilesPerKilogram = Nominal<number>;
var miles: Miles = 3; // error
var kilos: Kilograms = 3; // error
var milesPerKilos: MilesPerKilogram = x / y; // error
var x: Miles = 3 as Miles; // ok
var y: Kilograms = 3 as Kilograms; // ok
var u: MilesPerKilogram = x / y as MilesPerKilogram; // ok
miles = kilos; // ok, but it should be error! <================= This is due to declaration of Nominal<T>
Consider also https://basarat.gitbook.io/typescript/main-1/nominaltyping for more info
You are looking for nominal typing. Support is planned for future TypeScript versions (see Roadmap). For now, you have to use this: https://basarat.gitbooks.io/typescript/docs/tips/nominalTyping.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With