But I still wish I could say “this function accepts a type called RobotName. It’s a string, but so is RobotUuid, and we don’t want that. So only accept, strictly, objects typed as RobotName.”
But I still wish I could say “this function accepts a type called RobotName. It’s a string, but so is RobotUuid, and we don’t want that. So only accept, strictly, objects typed as RobotName.”
It felt like a lot of busywork, but it did prevent some classes of bugs.
Across the entire codebase, we discovered an entire class of bugs that only never cause any issues because all the important rows in all the important tables had Id 1 (e.g. Currency 1 was USD and Country 1 was USA) - so in a few places where the ints got mixed up, the correct row was still accidentally looked up in the wrong table).
The introduction of the additional types everywhere did turn into massive headaches based around dependency management and versioning.
So overall, I was personally disappointed in the results, but nevertheless happy to work with less "icky" feeling code.
It's very easy to mentally shrug and move on, but more often than not it comes back to bite you; maybe it's a code path that's rarely triggered, e.g.
interface RobotName {
value: string;
type: "RobotName";
}
Or if you don't want to make an extra wrapper around every object: interface RobotName {
_phantomType: "RobotName";
}
function makeRobotName(name: string): RobotName {
return name as any as RobotName;
}
function getRobotName(robotName: RobotName): string {
return robotName as any as string;
}
Presumably V8 is smart enough to inline the wrapper functions.Here there is an interesting article about doing this in typescript (not affiliated)
or some variation. The marker does not actually need to exist.
declare const isRobotID: unique symbol;
type RobotID = number & { [isRobotID]: true };
and now you can cast a number to a RobotID and back.function foo(r: RobotName) {}
?
type RobotUuid = string;
const bar: RobotUuid = “abc…”;
foo(bar);
This is what duck typing is and specifically my curiosity about being able to de-duck on demand.
Tho I suppose if it is really important you can put an assert there but I'm not familiar with that wrt typescript, maybe the transpiler would kill that?
I've done the occasional type checking in that way in similar languages, it is kind of self documenting too. 90% of the time duck typing is what you want.
assert istype(whatever, MyCustomType)
Which would throw an exception if whatever is not a "MyCustomType" at runtime.
Yes, and that’s roughly what TypeScript is: a linter that everyone on your team is running.