I guess working in the pure logic and formalism of types is just addictive.
I love it.
121–130 of 209 posts
I guess working in the pure logic and formalism of types is just addictive.
I love it.
Flow is much better with opaque types. Also nominal types for classes. And correct variance. And adhering to liskov substitution principles. And exact object types. And spread on types matching runtime behavior. And proper no transpilation mode with full access to the language. And has 10x less LoC than ts. ps. before somebody says "flow is dead" have a look at flow contributions [0] vs typescript contributions [1] […
Check out the react codebase, which is presumably the flagship use of flow. It has hundreds of FLOW FIXME annotations. It's easier have a lot of features and small code base if the you don't handle the difficult cases.
I expected thousands.
Anyone else sometimes get more sucked in to perfectly typing stuff instead of writing code. I guess working in the pure logic and formalism of types is just addictive. I love it.
Although if you do a bad job it might add a lot...
Earlier quoted context omitted.
`never` is a problematic field type, at least unless you make efforts (via non-exported symbols, for instance) to make sure the field is inaccessible - `never` is the type of a value that doesn't exist; it's there in the type system to signify an impossible scenario. For instance, a common use-case is to mark the type of a variable after type narrowing has exhausted every possible case. If you assert that your id's a…
Isn't that what you want to signify? It's the intent, and better than asserting that your IDs have a string-valued field that it doesn't. Ideally you could brand with a private field, but we would probably need `typeof class` for that (assuming `typeof class` allows private members. I'm not sure).
If by some mistake your code gets a reference to a field with a `never` type, you’ll never get a type error passing or assigning that reference downstream. That’s relatively trivial to address if you can spot the (edit: runtime) errors it causes directly, but can wreak terrible havoc if it finds a path through several layers of function calls or whatever other indirection before the runtime consequences surface.
Earlier quoted context omitted.
Ah, yeah the error makes sense. I expected the error, just wanted to understand how Brand was meant to be actually assigned to a primitive. I'm not sure the function is necessary though. This does the same thing const accountId = "125314" as AccountId It makes sense that the technique uses casting.
That's a very clear example. But what if I instead wrote: const accountId = AccountId ("125314" ); There would be the needed checks and balances in the function AccountId(). Wouldn't that do pretty much the same thing?
Isn't this just the classic issue of inferred typing coming back to bite us in the way everyone originally predicted? Go runs into the same issue where wildly different types may be considered the same based purely on coincidental naming and matching against interfaces the original authors had no intent to match against. At the end of the day I think the easier system to work with is one in which all type compatibili…
type UserId stringAnytime I’ve come across the need to do this, I’ve found a class is a better and less complicated solution. I really like the pattern of value objects from Domain Driven Design. Create a class that stores the value, for example email address. In the class constructor, take a string and validate it. Then anywhere that you need a valid email address, have it accept an instance of the Email class. As far as I understand…
type Email = Branded
function Email(maybeEmail: string): Email {
assertIsEmail(maybeEmail);
return maybeEmail;
}
function assertIsEmail(value: string): asserts value is Email {
if(!isEmail(value)) throw TypeError("Not an email");
return value;
}
function isEmail(value: string): value is Email {
return value.contains("@");
}
Versus: class Email {
#email: string;
public constructor(maybeEmail: string) {
if(!isEmail(maybeEmail)) throw TypeError("Not an email");
this.#email = maybeEmail;
}
valueOf() {
return this.#email;
}
}Earlier quoted context omitted.
There was plenty of outrage when classes were added to JS. “JS uses prototypal inheritance, not OOP!”
I'm still a bit cranky about that, but because objects should be good enough for anyone. They aren't even really classes anyway.
Isn't this just the classic issue of inferred typing coming back to bite us in the way everyone originally predicted? Go runs into the same issue where wildly different types may be considered the same based purely on coincidental naming and matching against interfaces the original authors had no intent to match against. At the end of the day I think the easier system to work with is one in which all type compatibili…