This can really blow up :) I think typescript is smart about ultimately collapsing a type when it gets too big (something like a few thousand options) "hey, its just a string - don't go crazy now"
Metadata based programming can be very very powerful. It essentially allows you to expand an application by just adding metadata to an object, while also writing less code overall. I have found that old codebasis often have similar and/or duplicate code all over the place, which can be refactored with this metadata based driven approach. Cuts down on TONS of boilerplate.
Thanks a lot for this longer explanation! I sometimes have a harder time catching up on the really short videos but here I was able to get everything immediately.
Excellent video! You often make videos about bleeding edge TS features (that I can’t use yet) or incredibly complex topics (that make me scratch my head) but this video was super accessible and easy to follow! Here’s hoping you will make more mid-level TS concepts videos.
Thanks man, 3 months ago I started learning expo, and I wanted to make routes, I suffered so much because of that, I was obligated to type routes as any...and now I know how to fix it❤
I love as const so much. It gives so much information of the code base you're trying to work with. It's makes localization strings global constants so much more useable.
@@scylk It's very simple. You create an object that basically stores all of your strings and add an handler to dynamically change it based on the users OS language. When you mark those strings as const, TypeScript still sees the values of your default language and it's easier to know what string represents what text.
The biggest issue I've had with 'as const' is that it doesn't play well with libraries. Because of the readonly you'll get into cases where libraries are expected something like string[] and will require casting to mask the readonly. It also doesn't actually freeze the values, which can cause hard to debug bugs
Why didnt you use enums for the routes? It looks like you only need one of the three routes at any moment. This particular example doesn't show use of as const i think. (I'm new to typescript so maybe I'm wrong, please correct me)
Right, why prefer the code in the video in place of a string enum? String enums are made for this, and they generate faster and generally smaller JS output. const enum Routes { Home = "/", Admin = "/admin", Users = "/users", }
@@programming5274 I totally agree yet I supose it is just to show that we have that option too. Imho u end with objects from other devs everytime so maybe u can just put 'as const' on them instead of refactoring to enums other people's job, idk
Nice video! Heads up on this format on mobile because there's no TH-cam outro, the "suggested video" card appeared whilst you were still coding and blocked the code in video
Thank you sir for this great explanation. I am glad that there are so passionate typescript writers out there like yourself. Just watching this video made me understand the keyof typeof stuff and as const much easier.
Hi Matt, I'm wondering if this prettify option could/should be better or not: export type ValueOf = Prettify; type Prettify = { [K in keyof T]: T[K]; } & {};
As someone who doesn't know TS and just knows C#, I recoiled slightly when I saw the keyof typeof [] bit But then I remembered reflection is pretty much like that anyways in C#, accessing public (or private) members and the like.
The difference is that reflection happens at runtime. This "as const" typing happens statically at compile time, so it's typesafe -- unlike reflection. On the other hand, typescript types are not reified, whereas .net types are. So reflection over typescript types isn't possible, because the type information is erased during compilation.
It would, but Matt has an irrational hatred for them, as seen in one of his videos "Why you shouldn't use Enums". Watch him abuse the concept of Enums and instead using magic variables. Biggest tool I've seen in TS.
is there a shorthand for this syntax (typeof variable)[keyof typeof variable]? just curious because it's this is going to be used a lot in the code base.
I don't get to just use typescript at work, but I sure as hell use jsdoc to the fullest in the meantime and love getting to do similar things with types. When you know for sure that you'll get auto completion and type errors, it's amazing how much less code you can end up writing. I recently set types up in such a way that I could safely turn 1000+ lines of manual references to properties on an object into just a few lines of code that auto updates, types and all.
Would you consider making a video/talk about the 'declare' keyword. While lurking into various .d.ts files I can spot all possible combinations of 'declare class', 'declare abstract class' (also: 'abstract class' alone). I have only used declare in typescript playground to mimic the existence of a function to make my types go trhough, but I can't get my head wrapped around real-life usage and how it is different from interfaces. Thanks!
Hey Matt, I've come across a pattern that I think deserves a shorthand: type X = { a: string } | { b : number } // Non discriminated union const x : X = { a: "hello"} const a = !!x && 'a' in x ? x.a : undefined // works fine, but is somewhat verbose. const a = x?.a // errors out because 'a' doesn't exist on type { b: number} Is there a simpler way of picking x.a?
What linter is "I noticed that routes has been declared, but it's never used in the code." a part of? It sounds much closer to natural language than usual error messages do.
So if you want as const but you don't want the whole tamale const you can define the internal object first as standard mutable object, and then put it inside in the outer const object. I.e. if you have const deep = { whatever: "/deep/whatever", }; const routes = { admin: "/admin", home: "/", deep: deep, } as const; then routes will be inferred as of type: { readonly admin: "/admin"; readonly home: "/"; readonly deep: { whatever: string; }; }
You mean const as opposed to var? That just means that the *reference* to the object is immutable, so the object can't be thrown away and replaced with a new object. It doesn't stop the contents of the object from being changed.
@@mattpocockuk checked your vid about enums, so again - why dont just use enum for this case? cause enum with specified values transpiles to a simple obj as you had in your video
I'm getting kinda imposter syndrome, please explain if we really use such tricky advanced concepts in coding, or is it okay if I'm using simple intermediate level code to get my stuff done, without engaging in fancy stuff like this. Btw, amazing video 🔥🔥!!
Like everything: It really depends. If you’re not a library developer but a consumer of libraries, and you’re able to develop comfortably without leaving much bugs in the code then you’re good. If you _do_ often encounter bugs _OR_ if you want to continue growing as a programmer (which I assume is what you’d want) it’s wise to learn these concepts so you’re prepared in the less common cases where you run into them, or so you have a broader background to make better decisions.
Question : But when compiled, the 'as const' Object won't be frozen to JS i guess. So, if it's for heap memory optimization it is better to freeze the object a long side 'as const', isn't it ?
Awesome explanation, thank you! Do you know if TS typesystem is Turing complete? Like the programming you can do at the type level like that? I bet it is.
Because, if you read the comments here, using Enums is bad for reasons most don't understand or are not impacted by in actuality. This hacky, non-intuitive solutions to a very basic problem is somehow superior, because... Well. It isn't. Glad this video doesn't disappoint, because Matt is such a massive tool. Simply look up his "Why you shouldn't use Enums" and waste minutes of your life watching a man glorify magic variables via transitive property instead of seeing Enums as what they are.
As const is really cool. Didn't know we could do that. Is there not an easier way to get values of an object as a union? Like we have keys of us there not values of
The thing about 'as const', when not used on type parameters, is that it lies. The runtime object is, in fact, not readonly. You can get the same effect, with added runtime correctness, from Object.freeze. I consider this an extension of the 'One Source of Truth' ethos. 'as const' is, essentially, a type assertion. But I'm %100 guilty of using it everywhere so 🤷
Yes multiple: Enums only fake being read-only while actually being changable. Enums create js code, while types won't (and a weird one too). Enums also are not as compatible with the typing algebra so at some point you will break it (e.g. a function takes in a parameter that is a subset of two enums)
@@casdf7 Is the last point correct? I've never had a function take a subset of two enums as a single parameter (don't think I ever will) but can't that be solved using typeof and type unions? If you meant a subset of a single enum which makes more sense, will something like `type Barbaz = typeof Foo.bar | typeof Foo.baz;` work?
Nice. Theo made a similar video of why Enums are bad. He did something similar like this object as const, but his was a string array as const. And the type Route = typeof routeArray[number]. So the difference is whether or not you want a key name associated with the routes
const obj = { username: 'abc', password: 'dce', email: 'fgh', } as const; let a: (typeof obj)[keyof typeof obj]; // Value of an object let b: Exclude; // Exclude a value from an object let c: keyof typeof obj; // Keys of an object let d: Exclude; // Exclude a key from an object
this i cool and all. but its actually just a workaround for an anti pattern., in the example case u give, why are u not using an enum type on routes, if fixed values are used ?
I use this all the time even though Object.freeze achieves the same effect with the benefit of actually enforcing const in JavaScript. While ‘as const’ is more readable, are there any other reasons for using it instead of Object.freeze? My guess is it depends on whether the object is exported as part of a library or not.
as const doesn't freeze the object - it is purely typescript - the object can still be modified but it will show a typescript error - if you want to be sure that the object won't be modified down the road you should use freeze
@@cristiang4774 Why would you expect to stop running the Typescript compiler down the road in your build pipeline? How is that any more likely than someone coming along and deleting the `object.freeze` call?
@@barneylaurance1865 You probably wouldn’t get rid of tsc, but there are edge cases in the type-checker that can allow the object to be mutated at runtime without raising a type error. Third party dependencies could also break things if they don’t use typescript, have incorrect .d.ts files, etc. Yeah, someone could remove `Object.freeze(…)`. They could also remove `as const`. Either way, tsc would complain , unless one was substituted for the other and then it’s (hopefully) flagged in a code review. IMO, it’s far better to have the runtime and compile-time behaviors align as much as possible. `Object.freeze(…)` is a few more keystrokes and a bit more verbose, but in the long-run, will protect your code as the codebase grows in size/complexity
You can also use `as const` on individual value literals or use it on template string literals to get all possible strings.
or on array to get a tuple. For instance typeof (["a", 1] as const) is readonly ["a", 1], while typeof ["a", 1] is (string | number)[]
The template literal trick is mind blowing!
This can really blow up :) I think typescript is smart about ultimately collapsing a type when it gets too big (something like a few thousand options) "hey, its just a string - don't go crazy now"
Metadata based programming can be very very powerful. It essentially allows you to expand an application by just adding metadata to an object, while also writing less code overall. I have found that old codebasis often have similar and/or duplicate code all over the place, which can be refactored with this metadata based driven approach. Cuts down on TONS of boilerplate.
You can also use 'satisfies' with 'as const' to constraint the object typings, like 'as const satisfies Record', its super neat
Damn I needed this so many times and didn't know about it, thanks for sharing.
holy crap I needed this, tysm
Thanks a lot for this longer explanation! I sometimes have a harder time catching up on the really short videos but here I was able to get everything immediately.
"const as const"
sounds really smart and not ridiculous at all
It makes perfect sense within the typescript syntax, and it's relationship with javascript syntax
Awesome! Thanks!
works on arrays as well:
const abc = ['a', 'b', 'c'] as const;
type ABC = typeof abc[number];
Just started picking up TypeScript and your videos are really clear and concise, thank you.
It's ridiculous that I can't expect const as constant value. Love your vids
You can, you just need to know what is constant about your value.
Excellent video! You often make videos about bleeding edge TS features (that I can’t use yet) or incredibly complex topics (that make me scratch my head) but this video was super accessible and easy to follow! Here’s hoping you will make more mid-level TS concepts videos.
After watching half of the video, I found an immediate use case in my TS project I'm working on. Great video!
Thanks man, 3 months ago I started learning expo, and I wanted to make routes, I suffered so much because of that, I was obligated to type routes as any...and now I know how to fix it❤
I love as const so much. It gives so much information of the code base you're trying to work with. It's makes localization strings global constants so much more useable.
Hey can you expand a little bit on that? Sounds interesting
@@scylk It's very simple. You create an object that basically stores all of your strings and add an handler to dynamically change it based on the users OS language. When you mark those strings as const, TypeScript still sees the values of your default language and it's easier to know what string represents what text.
What do you prefer “as const” or enum in cases like in video?
The biggest issue I've had with 'as const' is that it doesn't play well with libraries. Because of the readonly you'll get into cases where libraries are expected something like string[] and will require casting to mask the readonly.
It also doesn't actually freeze the values, which can cause hard to debug bugs
That's worthy of a medium article mate. Very nice solution to a very popular recurring problem
Can we use enum for store routes and avoid create additional types?
Just came to say this video finally helped make `as const` click for me. Thanks for the info, Matt!
Was using an enum for a radio group in Zod and discovered this. Never went through the trouble of understanding it, though. Thanks 👍🏿
Why didnt you use enums for the routes? It looks like you only need one of the three routes at any moment. This particular example doesn't show use of as const i think. (I'm new to typescript so maybe I'm wrong, please correct me)
Right, why prefer the code in the video in place of a string enum? String enums are made for this, and they generate faster and generally smaller JS output.
const enum Routes {
Home = "/",
Admin = "/admin",
Users = "/users",
}
/* String enum */
const enum Routes {
Home = "/",
Admin = "/admin",
Users = "/users",
}
const goToRoute = (route: Routes) => {
console.log(route);
}
goToRoute(Routes.Home);
console.log("----------------------");
/* as const */
const rts = {
home: "/",
admin: "/admin",
user: "/users",
} as const;
type Rte = (typeof rts)[keyof typeof rts];
const goToRt = (route: Rte) => {
console.log(route);
}
goToRt(rts.home);
@@programming5274 Yes!
Because Matt has a hate boner for them and prefers magic variables. Sharing a codebase with him can be only the first step of hell.
@@programming5274 I totally agree yet I supose it is just to show that we have that option too. Imho u end with objects from other devs everytime so maybe u can just put 'as const' on them instead of refactoring to enums other people's job, idk
Amazing video.
Such really good pedagogy, loved it, even if I was already very familiar with as const and the other notions sumed up.
I use this quite often, I don't think it's underrated, I think other things may just be rated too highly... LOL
PS: Where is the Top hat and Monocle?
@@ColinRichardsonPeople do wear two monocles to make a _spectacle_ for themselves, so now he's just missing the top hat, old chap 🎩
Nice video! Heads up on this format on mobile because there's no TH-cam outro, the "suggested video" card appeared whilst you were still coding and blocked the code in video
Sometimes I utilize this tool to manage database names, collections, roles, etc., enabling me to infer values instead of strings :).
Thank you sir for this great explanation. I am glad that there are so passionate typescript writers out there like yourself. Just watching this video made me understand the keyof typeof stuff and as const much easier.
You guys are lucky I'm dumb, I asked the question that inspired this video :)
Thanks for all you do Matt, I learned a whole lot.
Almost as good as 'as any'!
That's absolutely brilliant! I wasn't super sure what "as const" was doing, thanks for clarifying that!
Godly gift of explanation.
I love your typescript content, thanks
Needed this a few months ago 😂 excellent content on this channel dude keep it up
i was in a live stream a few days ago on this,learned so much
Hi Matt, I'm wondering if this prettify option could/should be better or not:
export type ValueOf = Prettify;
type Prettify = {
[K in keyof T]: T[K];
} & {};
The best and simplest explanation of "as const"!
As someone who doesn't know TS and just knows C#, I recoiled slightly when I saw the keyof typeof [] bit
But then I remembered reflection is pretty much like that anyways in C#, accessing public (or private) members and the like.
The difference is that reflection happens at runtime. This "as const" typing happens statically at compile time, so it's typesafe -- unlike reflection.
On the other hand, typescript types are not reified, whereas .net types are. So reflection over typescript types isn't possible, because the type information is erased during compilation.
Would an enum work the same?
It would, but Matt has an irrational hatred for them, as seen in one of his videos "Why you shouldn't use Enums". Watch him abuse the concept of Enums and instead using magic variables. Biggest tool I've seen in TS.
I did not know as const prevented the object being changed - thanks!
But what benefit it adds in comparison to string enums?🥺
Wondering the same lol
That you can use strings and it doesn't bother
YOU are the most underrated TH-camr
is there a shorthand for this syntax (typeof variable)[keyof typeof variable]? just curious because it's this is going to be used a lot in the code base.
This is such a helpful feature. I'll be using this all the time now, thank you
This channel is also underrated. awesome video! you just got a new subscriber here
"Object.values on a type level" trick is pretty neat. Thanks!
Normally I would an enum in this case like "enum routes" and avoid declaring the type. What do you think?
I don't get to just use typescript at work, but I sure as hell use jsdoc to the fullest in the meantime and love getting to do similar things with types. When you know for sure that you'll get auto completion and type errors, it's amazing how much less code you can end up writing. I recently set types up in such a way that I could safely turn 1000+ lines of manual references to properties on an object into just a few lines of code that auto updates, types and all.
Watched small video - huge power acquired. Thank you!
Would you consider making a video/talk about the 'declare' keyword. While lurking into various .d.ts files I can spot all possible combinations of 'declare class', 'declare abstract class' (also: 'abstract class' alone). I have only used declare in typescript playground to mimic the existence of a function to make my types go trhough, but I can't get my head wrapped around real-life usage and how it is different from interfaces. Thanks!
Really great.
You are a great teacher 😊
Thx from France.
How about routes with matching variables like '/users/:id' that match /users/3 ?
How can I type safe for api end point url for fetch ? Some of them will need query or other will need only body .
Hey Matt, I've come across a pattern that I think deserves a shorthand:
type X = { a: string } | { b : number } // Non discriminated union
const x : X = { a: "hello"}
const a = !!x && 'a' in x ? x.a : undefined // works fine, but is somewhat verbose.
const a = x?.a // errors out because 'a' doesn't exist on type { b: number}
Is there a simpler way of picking x.a?
Search for "ExclusifyUnion", there's a good stack overflow answer with an explanation
That was a whole lot of fantastic stuff that will immediately be used
What linter is "I noticed that routes has been declared, but it's never used in the code." a part of? It sounds much closer to natural language than usual error messages do.
It's the TypeScript error translator I built:
marketplace.visualstudio.com/items?itemName=mattpocock.ts-error-translator
@@mattpocockuk Oh neat
Amazing! I needed this earlier today!
you can just use an enum with string values?
Very nice ticks! keep them comming sensei!
And this is because, I am assuming from the pop-up, enums should not be used?
Question here. What's the difference between using "as const" and using "Readonly" utility type????????
With Readonly, you need to pass it a type. 'as const' operates on the value itself, so you can skip that step.
@@mattpocockuk oh so they serves the same purpose that is to make something immutable, but in a different way
Thank you very much Mr Matt!
Wow great video especially as a new typescript developer, I subscribed
What's the different between this and using Enums?
So if you want as const but you don't want the whole tamale const you can define the internal object first as standard mutable object, and then put it inside in the outer const object.
I.e. if you have
const deep = {
whatever: "/deep/whatever",
};
const routes = {
admin: "/admin",
home: "/",
deep: deep,
} as const;
then routes will be inferred as of type:
{
readonly admin: "/admin";
readonly home: "/";
readonly deep: {
whatever: string;
};
}
This is so beautiful that i want to cry.
This is so awesome! Thank you!
1:54 isn't const by definition a non-changing variable? i.e. immutable?
You mean const as opposed to var? That just means that the *reference* to the object is immutable, so the object can't be thrown away and replaced with a new object. It doesn't stop the contents of the object from being changed.
Awesome tip here. Thank you @mattpocockuk for sharing this.
Great video and example.
Why not using enum for this case ?
Check my video on enums
@@mattpocockuk checked your vid about enums, so again - why dont just use enum for this case? cause enum with specified values transpiles to a simple obj as you had in your video
I'm getting kinda imposter syndrome, please explain if we really use such tricky advanced concepts in coding, or is it okay if I'm using simple intermediate level code to get my stuff done, without engaging in fancy stuff like this. Btw, amazing video 🔥🔥!!
Like everything: It really depends. If you’re not a library developer but a consumer of libraries, and you’re able to develop comfortably without leaving much bugs in the code then you’re good.
If you _do_ often encounter bugs _OR_ if you want to continue growing as a programmer (which I assume is what you’d want) it’s wise to learn these concepts so you’re prepared in the less common cases where you run into them, or so you have a broader background to make better decisions.
Question : But when compiled, the 'as const' Object won't be frozen to JS i guess. So, if it's for heap memory optimization it is better to freeze the object a long side 'as const', isn't it ?
Finally... now I understand it. Thank you Matt.
You just saved me wtf. I was looking for a solution like this yesterday
Awesome explanation, thank you!
Do you know if TS typesystem is Turing complete? Like the programming you can do at the type level like that? I bet it is.
It is!
what is the VSCode extension you're using to get the human-readable ts error messages?
www.totaltypescript.com/vscode-extension
@@mattpocockuk Thanks so much!
I guess your example is similiar to the usage of enum (?). are there any other example besides that?
For the first example, why not just declare an enum of the route paths and pass the enum value?
In this case, why not use enums?
Because, if you read the comments here, using Enums is bad for reasons most don't understand or are not impacted by in actuality. This hacky, non-intuitive solutions to a very basic problem is somehow superior, because... Well. It isn't. Glad this video doesn't disappoint, because Matt is such a massive tool. Simply look up his "Why you shouldn't use Enums" and waste minutes of your life watching a man glorify magic variables via transitive property instead of seeing Enums as what they are.
I love your videos. Very insightful, fun, and straight to the point
You didn't talk about: "as const satisfies Record "
I started using a lot of "as const satisfies typeX", I'm creating a lib and it's helping me a lot
Please how can I get the TS translation feature on vs code?
probably Total Typescript
As const is really cool. Didn't know we could do that. Is there not an easier way to get values of an object as a union? Like we have keys of us there not values of
I am assuming this video came out before native enums existed on TS, right?
Now using rust and coming from c this is really funny to me how JS devs (i am a 4 years js/ts dev) go nuts with typing systems
Simply a typescript magician
Amazing so concise and clean.
Finally something from youtube I will actually use
The thing about 'as const', when not used on type parameters, is that it lies. The runtime object is, in fact, not readonly. You can get the same effect, with added runtime correctness, from Object.freeze. I consider this an extension of the 'One Source of Truth' ethos. 'as const' is, essentially, a type assertion.
But I'm %100 guilty of using it everywhere so 🤷
Videos are very useful and clear, thanks
what is that extension that translates ts errors i need it
probably Total Typescript
Is there an advantage over creating an enum if you just have a flat list of constant keys?
My thoughts exactly , is this not just enums?
Yes multiple:
Enums only fake being read-only while actually being changable.
Enums create js code, while types won't (and a weird one too).
Enums also are not as compatible with the typing algebra so at some point you will break it (e.g. a function takes in a parameter that is a subset of two enums)
@@casdf7 Is the last point correct? I've never had a function take a subset of two enums as a single parameter (don't think I ever will) but can't that be solved using typeof and type unions? If you meant a subset of a single enum which makes more sense, will something like `type Barbaz = typeof Foo.bar | typeof Foo.baz;` work?
Anybody knows that translation plugin for error messages ?
thank you, you are a life saver!
type Values = T[keyof T];
const routes = {
home: '/',
admin: '/admin',
users: '/users'
} as const;
type Route = Values; // Route = "/" | "/admin" | "/users"
Made me subscribe. Thank you very much. 👍
Oh cool! I knew it for array to union, but not for object to union. Thanks!
Niiiiice ! I always forget how to target the type of keys and values of objects 😅
Brilliant. Instant follow
Nice. Theo made a similar video of why Enums are bad. He did something similar like this object as const, but his was a string array as const. And the type Route = typeof routeArray[number]. So the difference is whether or not you want a key name associated with the routes
const obj = {
username: 'abc',
password: 'dce',
email: 'fgh',
} as const;
let a: (typeof obj)[keyof typeof obj]; // Value of an object
let b: Exclude; // Exclude a value from an object
let c: keyof typeof obj; // Keys of an object
let d: Exclude; // Exclude a key from an object
great video, thanks!!
this i cool and all. but its actually just a workaround for an anti pattern., in the example case u give, why are u not using an enum type on routes, if fixed values are used ?
Check out my video on enums
@@mattpocockuk instead of cloutserve ur enum video, just answer one of the 5k comments pointing out enums maybe ?
@@4w0ken I made an 8 minute video explaining my thoughts! That's a far better resource than a random buried comment.
@@mattpocockuk yeah fair
I use this all the time even though Object.freeze achieves the same effect with the benefit of actually enforcing const in JavaScript.
While ‘as const’ is more readable, are there any other reasons for using it instead of Object.freeze?
My guess is it depends on whether the object is exported as part of a library or not.
Should have watched a few more second before commenting 2:46 ¯\_(ツ)_/¯
as const doesn't freeze the object - it is purely typescript - the object can still be modified but it will show a typescript error - if you want to be sure that the object won't be modified down the road you should use freeze
@@cristiang4774 Why would you expect to stop running the Typescript compiler down the road in your build pipeline? How is that any more likely than someone coming along and deleting the `object.freeze` call?
@@barneylaurance1865 You probably wouldn’t get rid of tsc, but there are edge cases in the type-checker that can allow the object to be mutated at runtime without raising a type error. Third party dependencies could also break things if they don’t use typescript, have incorrect .d.ts files, etc.
Yeah, someone could remove `Object.freeze(…)`. They could also remove `as const`. Either way, tsc would complain , unless one was substituted for the other and then it’s (hopefully) flagged in a code review.
IMO, it’s far better to have the runtime and compile-time behaviors align as much as possible. `Object.freeze(…)` is a few more keystrokes and a bit more verbose, but in the long-run, will protect your code as the codebase grows in size/complexity