4 ms·
In the context of type definitions, strings aren't really regular bare strings. I.e. if I write type foo = { bar: "Vodka" } I'm guaranteeing that th
by p1necone 4y ago
In the context of type definitions, strings aren't really regular bare strings.
I.e. if I write
type foo = {
bar: "Vodka"
}
I'm guaranteeing that the value of 'bar' on any object of that type must be "Vodka" - the type of bar is not string, it's literally "Vodka" because string values can be types.
This seems a little obscure and pointless, but you can put these string types in a union, and enforce that a value must be one of many values.
function doTheThing(color: "RED" | "GREEN") {
//do stuff
}
doTheThing("RED"); //ok
doTheThing("GREEN"); //ok
doTheThing("ORANGE"); //doesn't compile, because the type of the param *isn't* string, it's "RED" | "GREEN"
You can also define a type like
type Mountain = { name: "EVEREST", height: 8848 } | { name: "K2", height: 8611 };
and then at compile time know that if mountain.name === "EVEREST" then height === 8848 because the types of name and height aren't string and number, they're "EVEREST" | "K2" and 8848 | 8611, and the compiler is smart enough to work out one based on the other.
==============================================
For extra context - a lot of this is for interoptability with Javascript code - you want to call some Javascript function with a stringly typed enum, and enforce only passing in valid values, but the Javascript code still just deals with strings.
A lot of Typescripts kind of insane flexibility is so you can introduce type safety to all sorts of dynamic Javascript code, without having to make sacrifices on the dynamic-ness of it.
Turns out this flexibility is actually kinda awesome to have in general even when you're not trying to refactor an existing JS codebase.
- RussianCow 4y agoTo add to this, the reason it’s important for TypeScript to support string literals as values instead of using enums or something new is interop with existing JS. For example, think of an event handler where you pass the event name as the first argument; you can ensure at compile-time that an invalid event name isn’t passed.
- chrismorgan 4y agoOr more importantly, varying the event type based on the event name. e.g. addEventListener("click", (MouseEvent): void) versus addEventListener("input", (InputEvent): void)
- bobbylarrybobby 4y agoAnd don't forget about [template literal types](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#template-literal-types https://devblogs.microsoft.com/typescript/announcing-typescr...)! ``` type Color = "red" | "blue"; type Quantity = "one" | "two"; type SeussFish = `${Quantity | Color} fish`; // same as // type SeussFish = "one fish" | "two fish" // | "red fish" | "blue fish"; ```
- Sirenos 4y agoThat's really interesting. doTheThing(readFromUser()) What does the compiler do in cases where you have strings coming in like this?
- paavohtl 4y agoIt gives a type error, because strings are not assignable to string literals. https://www.typescriptlang.org/play?#code/FAEwpgxgNghgTmABAMwK4DsIBcCWB7dREPAFQAsxyd0BzACgjyjzgC5EAiAJQFEARDogA+nAOK8eAOQ4BKdgDc8OEKEiwEKDNnyEEMEADE4eALYBVAM5g4dOYgtY41GsFCkKVWnT2Hj5qzYyMsBAA https://www.typescriptlang.org/play?#code/FAEwpgxgNghgTmABAM... However, if you check or assert that the returned value is one of the accepted literals, compiler accepts it: https://www.typescriptlang.org/play?#code/FAEwpgxgNghgTmABAMwK4DsIBcCWB7dREPAFQAsxyd0BzACgjyjzgC5EAiAJQFEARDogA+nAOK8eAOQ4BKdgDc8OEKEiwEKDNnyEEMEADE4eALYBVAM5g4dOYgtY41GsGCN0DxNQAOqLIgBeRD1DY3MrGxlXHGREOh8-QICggHJePhThEQT-ZNTxHikUmUQAb2BEIlIKKlp49F8sKIBfYCA https://www.typescriptlang.org/play?#code/FAEwpgxgNghgTmABAM... This is one of the classic examples of flow-sensitive typing in TS.
- phpnode 4y agoit'll tell you that `string` cannot be assigned to type `"RED" | "GREEN"`, and so you'll need to write a function that refines the type correctly, e.g. function isValidInput(input: unknown): input is "RED" | "GREEN" { return input === "RED" || input === "GREEN"; } const input = readFromUser(); if (isValidInput(input)) { doTheThing(input); // no type error }
- moron4hire 4y agoIt's an error, as the plain `string` type doesn't satisfy the union. You'll need to perform type assertions to make the return type down before passing it as a parameter.
- Joker_vD 4y agoThe academic term for such types is "singleton types". They allow for some quite mind-blowing possibilities as you've shown.