> TypeScript is a wonderfully advanced language though it has an unfortunately steep learning curve
An extremely steep one.
The average multi-year TypeScript developer I meet can barely write a basic utility type, let alone has any general (non TypeScript related) notion of cardinality or sub typing. Hell, ask someone to write a signature for array flat, you'd be surprised how many would fail.
Too many really stop at the very basics.
And even though I consider myself okay at TypeScript, the gap with the more skilled of my colleagues is still impressively huge.
I think there's a dual problem, on one side type-level programming isn't taken seriously by the average dev, and is generally not nurtured.
On the other hand, the amount of ideas, theory, and even worse implementation details of the TypeScript compiler are far from negligible.
Oh, and it really doesn't help that TypeScript is insanely verbose, this can easily balloon when your signatures have multiple type dependencies (think composing functions that can have different outputs and different failures).
I don’t think that means it has a steep learning curve. It just means the basics suffice for a ton of TypeScript deployments. Which I personally don’t see as the end of the world.
Yes, to me this is a biggest feature of Typescript: A little goes a long way, while the advanced features make really cool things possible. I tend to think of there being two kinds of Typescript - Application Typescript (aka The Basics, `type`, `interface`, `Record`, unions etc...) and Library Typescript which is the stuff that eg Zod or Prisma does to give the Application Typescript users awesome features.
While I aspire to Library TS levels of skill, I am really only a bit past App TS myself.
On that note I've been meaning to the the Type-Level Typescript course [0]. Has anyone taken it?
is far from basic Typescript. The average Typescript dev likely doesn't need to understand recursive conditional types. It's a level of typescript one typically only needs for library development.
Not only have I never been expected to write something like this for actual work, I'm not sure it's been useful when I have, since most of my colleagues consider something like this nerd sniping and avoid touching/using such utilities, even with documentation.
If I saw that in a PR I would push very hard to reject; something like that is a maintenance burden that probably isn’t worth the cost, and I’ve been the most hardcore about types and TypeScript of anyone of any team I’ve been on in the past decade or so.
Now, that said, I probably would want to be friends with that dev. Unless they had an AI generate it, in which case the sin is doubled.
these are things most developers don't know how to do in most language's type systems. I think only rust with its focus on functional roots has seen similar focus on utilizing its type system to its fullest extent.
TypeScript codebases I've seen generally seem to have the widest demonstration of skill gap versus other languages I use.
For example, I don't ever see anyone using `dynamic` or `object` in C#, but I will often see less skilled developers using `any` and `// @ts-ignore` in TypeScript at every possible opportunity even if it's making their development experience categorically worse.
For these developers, the `type` keyword is totally unknown. They don't know how to make a type, or what `Omit` is, or how to extend a type. Hell, they usually don't even know what a union is. Or generics.
I sometimes think that in trying to just be a superset of JavaScript, and it being constantly advertised as so, TypeScript does not/did not get taken seriously enough as a standalone language because it's far too simple to just slot sloppy JavaScript into TypeScript. TypeScript seems a lot better now of having a more sane tsconfig.json, but it still isn't strict enough by default.
This is a strong contrast with other languages that compile to JavaScript, like https://rescript-lang.org/ which has an example of pattern matching right there on the home page.
Which brings me onto another aspect I don't really like about TypeScript; it's constantly own-goaling itself because of it's "we don't add anything except syntax and types" philosophy. I don't think TypeScript will ever get pattern matching as a result, which is absurd, because it has unions.
I have mixed feelings about Typescript, I hate reading code with heavy TS annotations because JS formatters are designed to keep line widths short, so you end up with a confusing mess of line breaks. Pure JS is also just more readable.
Also you can so easily go overboard with TS and design all sorts of crazy types and abstractions based on those types that become a net negative in your codebase.
However it does feel really damn nice to have it catch errors and give you great autocomplete and refactoring tooling.
Honestly I just use TypeScript to prevent `1 + [] == "1"` and check that functions are called with arguments. I don't care about type theory at all and the whole thing strikes me as programmers larping (poorly) as mathematicians.
then you're creating a giant mess of a soup where the state of your program could have a result, be loading and an error at the same time. If you could recognise that the state of your program is a sum of possible states (loading | success | error), and not their product as the type above you could highly simplify your code, add more invariants and reduce the number of bugs.
And that is a very simple and basic example, you can go *much* further, as in encoding that some type isn't merely a number through branded types, but a special type of number, be it a positive number between 2 and 200 or, being $ or celsius and avoiding again and entire class of bugs by treating everybody just as an integer or float.
This is wordier than just "as const", what advantage does it give? (I am a newbie and genuinely don't know)
edit: perhaps the advantage only comes into play for mutable values, where you want a narrower type than default, but not that narrow. Indeed, this is covered in the article, but CTRL+F "as const" doesn't work on the page for whatever reason, so I missed it.
The satisfies keyword is quite different than "as const." What it does is:
1. Enforce that a value adheres to a specific type
2. But, doesn't cause the value to be cast to that type.
For example, if you have a Rect type like:
type Rect = { w: number, h: number }
You might want to enforce that some value satisfies Rect properties... But also allow it to have others. For example:
const a = { x: 0, y: 0, w: 5, h: 5 };
If you wrote it as:
const a: Rect = // ...
TypeScript wouldn't allow you to also give it x and y properties. And if you did:
as Rect
at the end of the line, TypeScript would allow the x, y properties, but would immediately lose track of them and not allow you to use them later, because you cast it to the Rect type which lacks those properties. You could write an extra utility type:
type Location = { x: number, y: number };
const a: Location & Rect = // ...
But that can get quite verbose as you add more fields. And besides: in this example, all we actually are trying to enforce is that the object is a Rect — why do we also have to enforce other things at the same time? Usually TS allows type inference for fields, but here, as soon as you start trying to enforce one kind of shape, suddenly type inference breaks for every other field.
The satisfies keyword does what you want in this case: it enforces the object conforms to the type, without casting it to the type.
const a = { x: 0, y: 0, w: 5, h: 5 } satisfies Rect;
// a.x works
I've really only found benefit on the return type of functions, when you can say that a type parameter satisfies a type (with the return type being a boolean). This let's you use `if (isPerson(foo))` and typescript will narrow the type appropriately in the conditional
I’m so frustrated by satisfies because it eliminates optional properties.
I want an object of ‘LayerConfig’ elements where each key is the name of a possible layer. Without ‘satisfies’ I have to name every layer twice in my config. But with it, I can’t have optional properties (eg. Half the layers are fine with the default values for some properties).
The best I’ve found is a hack that uses a function. But this whole thing where my key literals widen into “string” is a constant annoyance to otherwise very elegant code.
> TypeScript is a wonderfully advanced language though it has an unfortunately steep learning curve
An extremely steep one.
The average multi-year TypeScript developer I meet can barely write a basic utility type, let alone has any general (non TypeScript related) notion of cardinality or sub typing. Hell, ask someone to write a signature for array flat, you'd be surprised how many would fail.
Too many really stop at the very basics.
And even though I consider myself okay at TypeScript, the gap with the more skilled of my colleagues is still impressively huge.
I think there's a dual problem, on one side type-level programming isn't taken seriously by the average dev, and is generally not nurtured.
On the other hand, the amount of ideas, theory, and even worse implementation details of the TypeScript compiler are far from negligible.
Oh, and it really doesn't help that TypeScript is insanely verbose, this can easily balloon when your signatures have multiple type dependencies (think composing functions that can have different outputs and different failures).
> Too many really stop at the very basics.
I don’t think that means it has a steep learning curve. It just means the basics suffice for a ton of TypeScript deployments. Which I personally don’t see as the end of the world.
Yes, to me this is a biggest feature of Typescript: A little goes a long way, while the advanced features make really cool things possible. I tend to think of there being two kinds of Typescript - Application Typescript (aka The Basics, `type`, `interface`, `Record`, unions etc...) and Library Typescript which is the stuff that eg Zod or Prisma does to give the Application Typescript users awesome features.
While I aspire to Library TS levels of skill, I am really only a bit past App TS myself.
On that note I've been meaning to the the Type-Level Typescript course [0]. Has anyone taken it?
https://type-level-typescript.com/
> Hell, ask someone to write a signature for array flat, you'd be surprised how many would fail.
To be clear, an array flat type:
is far from basic Typescript. The average Typescript dev likely doesn't need to understand recursive conditional types. It's a level of typescript one typically only needs for library development.Not only have I never been expected to write something like this for actual work, I'm not sure it's been useful when I have, since most of my colleagues consider something like this nerd sniping and avoid touching/using such utilities, even with documentation.
If I saw that in a PR I would push very hard to reject; something like that is a maintenance burden that probably isn’t worth the cost, and I’ve been the most hardcore about types and TypeScript of anyone of any team I’ve been on in the past decade or so.
Now, that said, I probably would want to be friends with that dev. Unless they had an AI generate it, in which case the sin is doubled.
these are things most developers don't know how to do in most language's type systems. I think only rust with its focus on functional roots has seen similar focus on utilizing its type system to its fullest extent.
TypeScript codebases I've seen generally seem to have the widest demonstration of skill gap versus other languages I use.
For example, I don't ever see anyone using `dynamic` or `object` in C#, but I will often see less skilled developers using `any` and `// @ts-ignore` in TypeScript at every possible opportunity even if it's making their development experience categorically worse.
For these developers, the `type` keyword is totally unknown. They don't know how to make a type, or what `Omit` is, or how to extend a type. Hell, they usually don't even know what a union is. Or generics.
I sometimes think that in trying to just be a superset of JavaScript, and it being constantly advertised as so, TypeScript does not/did not get taken seriously enough as a standalone language because it's far too simple to just slot sloppy JavaScript into TypeScript. TypeScript seems a lot better now of having a more sane tsconfig.json, but it still isn't strict enough by default.
This is a strong contrast with other languages that compile to JavaScript, like https://rescript-lang.org/ which has an example of pattern matching right there on the home page.
Which brings me onto another aspect I don't really like about TypeScript; it's constantly own-goaling itself because of it's "we don't add anything except syntax and types" philosophy. I don't think TypeScript will ever get pattern matching as a result, which is absurd, because it has unions.
I have mixed feelings about Typescript, I hate reading code with heavy TS annotations because JS formatters are designed to keep line widths short, so you end up with a confusing mess of line breaks. Pure JS is also just more readable.
Also you can so easily go overboard with TS and design all sorts of crazy types and abstractions based on those types that become a net negative in your codebase.
However it does feel really damn nice to have it catch errors and give you great autocomplete and refactoring tooling.
Honestly I just use TypeScript to prevent `1 + [] == "1"` and check that functions are called with arguments. I don't care about type theory at all and the whole thing strikes me as programmers larping (poorly) as mathematicians.
I couldn't care less about mathematics, but I do care about making impossible state impossible and types documenting the domain.
If you type some state as:
then you're creating a giant mess of a soup where the state of your program could have a result, be loading and an error at the same time. If you could recognise that the state of your program is a sum of possible states (loading | success | error), and not their product as the type above you could highly simplify your code, add more invariants and reduce the number of bugs.And that is a very simple and basic example, you can go *much* further, as in encoding that some type isn't merely a number through branded types, but a special type of number, be it a positive number between 2 and 200 or, being $ or celsius and avoiding again and entire class of bugs by treating everybody just as an integer or float.
This is wordier than just "as const", what advantage does it give? (I am a newbie and genuinely don't know)
edit: perhaps the advantage only comes into play for mutable values, where you want a narrower type than default, but not that narrow. Indeed, this is covered in the article, but CTRL+F "as const" doesn't work on the page for whatever reason, so I missed it.
The satisfies keyword is quite different than "as const." What it does is:
1. Enforce that a value adheres to a specific type
2. But, doesn't cause the value to be cast to that type.
For example, if you have a Rect type like:
You might want to enforce that some value satisfies Rect properties... But also allow it to have others. For example: If you wrote it as: TypeScript wouldn't allow you to also give it x and y properties. And if you did: at the end of the line, TypeScript would allow the x, y properties, but would immediately lose track of them and not allow you to use them later, because you cast it to the Rect type which lacks those properties. You could write an extra utility type: But that can get quite verbose as you add more fields. And besides: in this example, all we actually are trying to enforce is that the object is a Rect — why do we also have to enforce other things at the same time? Usually TS allows type inference for fields, but here, as soon as you start trying to enforce one kind of shape, suddenly type inference breaks for every other field.The satisfies keyword does what you want in this case: it enforces the object conforms to the type, without casting it to the type.
Then if someone edits the code to: TypeScript will throw an error, since it no longer satisfies the Rect type (which wants h and w, not height and width).Thanks; succinct and for me, I understood it.
I've really only found benefit on the return type of functions, when you can say that a type parameter satisfies a type (with the return type being a boolean). This let's you use `if (isPerson(foo))` and typescript will narrow the type appropriately in the conditional
With as const you can’t verify against another interface
Why? why make your code so complex you even hit this problem. Just use the type:
const x: Thetype = ....
I am not keen on as const either. Just program to interfaces. It is a better way to think IMO.
The second example confuses me. The Person type has isCool: boolean, not an explicit true. How does using satisfies here pass coolPeopleOnly?
You can sorta think of `satisfies Foo` as "the type is exactly the literal value, but also make sure the value could be used in the place of a Foo"
I’m so frustrated by satisfies because it eliminates optional properties.
I want an object of ‘LayerConfig’ elements where each key is the name of a possible layer. Without ‘satisfies’ I have to name every layer twice in my config. But with it, I can’t have optional properties (eg. Half the layers are fine with the default values for some properties).
The best I’ve found is a hack that uses a function. But this whole thing where my key literals widen into “string” is a constant annoyance to otherwise very elegant code.
Then either make the properties optional or use Partial on the type you are satisfying
> This keyword is a bit esoteric and not very common, but it comes in handy in some scenarios where you’d otherwise pull your hair out.
Typescript in a nutshell. That said, satisfies is a good keyword!
the cool thing about Typescript is that you never have to know any of this to deliver highly performant enterprise scale software
You can do that with assembly and not know ANY high level language.
You might be interested in reading PG's treatise on "the blub paradox".