The TypeScript feature I never use

  Рет қаралды 11,812

Andrew Burgess

Andrew Burgess

Күн бұрын

TypeScript gives us several ways to do type assertions, but they aren't all equal. Any time you're stepping over the TS compiler's choices, you might be creating a foot gun for later. Let's talk about why this is, and several ways to avoid it.
Linked Mentioned in the Video
Unconditional Code by Michael Feathers: • Unconditional Code • M...
My Links
shaky.sh
shaky.sh/tools

Пікірлер: 69
@Guergeiro
@Guergeiro Жыл бұрын
I also use your approach and try to avoid `!` or the `as` keyword as much as possible, but one thing that people normally forget to mention is, with the examples you gave (isMember, assertsMember, isNotNull) or type checking/narrowing is general, that you are introducing runtime behaviour to fix a compile time problem. That type checking/narrowing doesn't come for free and sometimes I see people checking deeply nested properties and such just to get to the type that satisfies TypeScript enough. You are correct in that one normally relies on these checks when at the edge of the system, but even then, although I understand safe guarding against client code, I think that when you are in control of a specific end to end edge of the system, I would argue `!` and `as` to be a nice way to save some cycles! Great vid btw! Keep at it!
@jasepellerin3350
@jasepellerin3350 Жыл бұрын
I agree. Knowing when to reach for these tools is an important lesson. Using 'as' or ! can simplify things, but makes your code less typesafe and may introduce bugs. If I know for certain that `array.find` will return an item, it may make sense to "cast" the result (usually to save time so I can finish the feature working on). However, I might kick myself later if an edge case happens to return undefined.
@antonpieper
@antonpieper Жыл бұрын
"Micro optimization is the root of all evil" ironically I do that kind of assertion in my code as well when I 100% know that it is safe because all other variables are controlled and safe
@Guergeiro
@Guergeiro Жыл бұрын
@@antonpieper it's not micro optimization if you're doing these checks inside a .reduce for example. Of course it depends, but the point I'm trying to make here is that we are solving a compiler problem by introducing runtime behaviour and almost no one mentions the bad parts, only good parts.
@amans6504
@amans6504 Жыл бұрын
We are over killing it with those runtimes checks. Just ship and move. Developer efficiency is more important.
@doremicocoparis9410
@doremicocoparis9410 Жыл бұрын
How is fixing bugs caused by type unsoundness good for developer productivity?
Жыл бұрын
for `findOrThrow`, a safer way to write the function would be `if (t === null) {...}` since we might be looking for something that resolves to 0.
@zacbackas
@zacbackas Жыл бұрын
To check for undefined wouldn't you want `== null` instead?
@andrew-burgess
@andrew-burgess Жыл бұрын
yep, `t == null` would definitely be a better way to do this, good callout!
@kbsanders
@kbsanders Жыл бұрын
6:16 Same caveat applies for line 17 and 24 (!!arg)
@kbsanders
@kbsanders Жыл бұрын
I guess line 17 is OK, since you're ultimately drilling down into an object.
Жыл бұрын
@@zacbackas I thought `Array.find` returned null when it did not match any results, but it does seem to return undefined so you're right. I guess you could even triple equal for undefined.
@pastenml
@pastenml Жыл бұрын
I find it very hard to be able to escape `as`. For example fetching JSON from an API where you know the shape they are in, working with the dom and events (event.target), using libraries that are poorly typed (like the chromecast types on definitelytyped which are both incomplete and wrong).
@allan4386
@allan4386 Жыл бұрын
I've found validating those 'unknown' types to be a good way to handle those cases, with something like zod
@doremicocoparis9410
@doremicocoparis9410 Жыл бұрын
Yeah I use zod for this, trust but verify
@necrifed
@necrifed 11 ай бұрын
Everyday I hate typescript more 😂
@StephenMoreira
@StephenMoreira Жыл бұрын
Thanks for your thoughts. Always love hearing the fundamental perspective of other developers.
@meyou118
@meyou118 Жыл бұрын
good advise - thx
@ricko13
@ricko13 Жыл бұрын
can you guys imagine the amount of hours this guy has put into typescript to get to where he is now? As a beginner myself, I feel like he's speaking chinese
@QckSGaming
@QckSGaming Жыл бұрын
Really just a few projects after a solid programming (not javascripting) background of some years. Generally languages have types so Typescript is pretty self-explanatory and you can see and learn the Typescript-isms when you google for common stuff like "typescript assert type is not *"
@ricko13
@ricko13 Жыл бұрын
@@QckSGaming well, kinda... if you really want to master this thing you gotta spend a lot of time working on it
@moodynoob
@moodynoob Жыл бұрын
​@@ricko13 You're correct, you need to go down the rabbit hole to truly make the most of Typescript (or if you maintain libraries). I use Typescript to encode actual business logic, and its cool to see multiple files full of types that will never make it to the user, but catch incorrect logic for my context.
@andrew-burgess
@andrew-burgess Жыл бұрын
@potato super-curious to see an example of how you encode business logic in TS, if you have one you can share!
@RobertKonigsberg
@RobertKonigsberg Жыл бұрын
`asserts arg is Type` is new to me. Very nice.
@Azoraqua
@Azoraqua Жыл бұрын
You can cast an object (Perhaps even an array) using generics. ``` type Person = { name: string, age: number, } const person = { name: 'John', age: 47, }; ``` Personally, I like this syntax quite a lot however it's effectively the same as `person: Person`. unlike `as` which doesn't force said format.
@guillaumebrunerie
@guillaumebrunerie Жыл бұрын
This has nothing to do with generics, this is just an alternative syntax to using "as". And it's recommended not to use this syntax anymore as it does not work with tsx.
@Azoraqua
@Azoraqua Жыл бұрын
@@guillaumebrunerie It’s different to the ‘as’ syntax because when using ‘as’ you’re overriding whatever the type is whilst that kinda generic-like syntax enforces said format instead of transforming it into another. Beyond that, I think it’s a decent option if you do not want to use ‘as’. In regard to lack of support with TSX, that’s only mildly inconvenient as you do not have to mix TS and TSX in the same files at all times.
@hynekss8618
@hynekss8618 Жыл бұрын
@@guillaumebrunerie It can be used in a tsx file, but you need to append a trailing comma, e.g., `const person = (...) {...}`
@gordonfreimann
@gordonfreimann Жыл бұрын
I couldn't help but notice the color of your ears change as the video progresses. On the other hand I really enjoyed the video and I think it is very helpful.
@ness-ee
@ness-ee 11 ай бұрын
Another one of your videos that I’ll show to the team.
@killymxi
@killymxi Жыл бұрын
Checked where I'm using it: import/export { foo as bar }; [] as T[]; {} as T when {} is a valid instance of T; Some situation with co/contra variance in higher order function overloading; Some situation when composing multiple generic higher order functions and it is too difficult for TS to infer the correct (still generic) type. Thanks to this video, I realized I no longer need some other instances of 'as' in my code and there is one more I can potentially remove by improving a type definition and allowing TS to generalize all argument types to a common type.
@killymxi
@killymxi Жыл бұрын
Just added several more instances to my code, after a user reported an issue that turned out to be a bad passed value in JS client code: - an input validation "!(typeof str === 'string' || (str as unknown) instanceof String)", because TS is not happy about instanceof after a "known" type; - a test with "as unknows as T" that expects a throw in case of invalid input.
@5argan
@5argan Жыл бұрын
For your first example, I use "const Foo: T[] =[];" instead
@oscarcastillejo9685
@oscarcastillejo9685 Жыл бұрын
Another way to see the “as const” is to declare a literal
@tzuilee588
@tzuilee588 Жыл бұрын
Your explanation is so good! 😁
@wtl912
@wtl912 3 ай бұрын
Excellent video, thanks a lot! Btw, it'd be great if you have the chance to add timestamps to your videos, it really helps a lot!
@aram5642
@aram5642 Жыл бұрын
`As` soon `as`... (pun intended!) one needs to use .querySelector combined with other DOM methods they become friends with `as`. Every HTML element has typically an inheritance hierarchy of at least 3 levels: Node, Element, HTMLElement, and so on. Depending on where you need the result you might have to use `as` to achieve a proper coercion. As for `asserts something is Something` - I personally avoid these, for exactly the same reason you mention: there is never a 100% certainty, and if so - I would like to avoid throwing errors.
@pawekoaczynski4505
@pawekoaczynski4505 Жыл бұрын
I use `as` in tests, when I don't want to pass all the fields of a type in a particular test. I never use `as` outside test (obviously, excluding the `as const`). Also, that `isMember` function might lie to you in the future. Let's say, two weeks later, somebody adds field 'foo'. And you don't update the `isMember` function. In that case, the function wil lie. The problem is that both `as` and `is` are a programmer's promise that the they know better than the compiler. In order to be 100% sure, you have to use a library like zod. The problem is, that it might be too heavy to be used in the browser
@ThaRealIansanity
@ThaRealIansanity Жыл бұрын
Thanks. I 've been trying to figure out how to deal with a complicated union of types returned from a nuxt composable. I think these methods will help.
@edgeeffect
@edgeeffect 26 күн бұрын
I've found myself a few times needing to do `as unknown as SomethingElse` and that leaves a horrible smell in the room... but, at my current stage, I don't see any way around it.
@neilpadfield
@neilpadfield Жыл бұрын
I find that `as` is sometimes necessary in certain generic functions or classes where it is 100% certain that something has to be a specific time, but the compiler can't work that out, so you have to tell it.
@KirkWaiblinger
@KirkWaiblinger Жыл бұрын
Just use a general assertion function to assert it's not null. Then you get narrowing. Declare function assert(x: unknown): asserts x; assert(x != null) This also doesn't address the common (anti)pattern of type widening assertions. const myRef = ref(null as null | HtmlElement); This is better to workaround with const myRef = ref(null)
@magicbob8
@magicbob8 Жыл бұрын
This is a great video!
@i.j.5513
@i.j.5513 Жыл бұрын
++ On the need to use assertions for when we are dealing with poorly typed 3rd party libraries. They could be typed to return any, but it is clear from the source code or documentation that they should return a string. You use the assertion there, because you're not really trying to test the 3rd party library code correctness. Why you would need to use that poorly typed 3rd party library or if there is an alternative to use --- that's a different question.
@najlepszyinformatyk1661
@najlepszyinformatyk1661 Жыл бұрын
What is the difference between just const and declare const?
@camotubi
@camotubi Жыл бұрын
Me yolo swaging with "as unknown as T"
@wavecoders
@wavecoders Жыл бұрын
Just add || 0 at the end of your find statement and if undefined it will make the return 0 as default
@whotao6259
@whotao6259 Жыл бұрын
I like to to use `function isNotNull(x: T): x is NonNullable` !
@yfzhangphonn
@yfzhangphonn Жыл бұрын
That's when you need Functional programming. ^^
@mk72v2oq
@mk72v2oq Жыл бұрын
Enable "noUncheckedIndexedAccess" compiler option for your codebase and enter a whole new world xD
@gosnooky
@gosnooky Жыл бұрын
Just a question not related to the topic - what is with your font? At 4:53, the "find" part looks squished, as if a fixed-width font has some variable-width characters. It's very strange and I'm just curious.
@artist6000ish
@artist6000ish 11 ай бұрын
Casting comes from 'C. It's not a runtime behavior. idk where you get that casting is a runtime behavior.
@amans6504
@amans6504 Жыл бұрын
We eventually need rutime checks at many such places
@tahasoft1
@tahasoft1 Жыл бұрын
I use this function export function assertIsDefined(x: T): asserts x is NonNullable { if (x === undefined || x === null) { throw new Error(`${Object.keys({ val: x })[0]} undefined`); } } for all places I want to assert something is not undefined or null for example const member = members.find(m => m.id === 1) assertIsDefined(member)
@KlethonioFerreira
@KlethonioFerreira 8 күн бұрын
There is react-router-dom, it's challenging....
@FunctionGermany
@FunctionGermany Жыл бұрын
9:00 zod
@me_rinta
@me_rinta Жыл бұрын
Why do you use “declare” for the first two statements?
@andrew-burgess
@andrew-burgess Жыл бұрын
It's a way to declare a variable of a given type in TS without needing to provide a value for it. I think of it as saying "suppose I have some variable called X of type Y" and then you can use X to see how it interacts with your types.
@kevduc314
@kevduc314 Жыл бұрын
Could you also make a similar video about using truthy/falsy values? 😄 They can sometimes be handy (e.g. `.filter(Boolean)`) but in general should probably be avoided because of how unreliable they are: e.g. in your code you used `!!` a few times, and it's especially deceptive when used with primitive types other than objects, like in isNotNull, that would actually return false if the input is the number 0 or empty string "", when what you really want is something that is neither null nor undefined. In general explicit checks with operators that don't coerce values to truthy/falsy are much safer, in this case explicitly checking for null or undefined with `!==` would be the way to go, `arg !== null && arg !== undefined`. And in that case TS doesn't save you from this JS mess that truthy/falsy values are: TS doesn't have more granular union exclusions on primitive types like string and number, e.g. it won't show you that by checking !!arg for a string, the resulting type for isNotNull would be something like `Exclude`, that just gets simplified to `string`, which doesn't help you catch the issue!
@ness-ee
@ness-ee 11 ай бұрын
I was wondering why he left that !!arg in. I’m gonna go and log out !!0 now to see. Ok I’m back. It’s false; so is an empty string. Tut tut
@hugodsa89
@hugodsa89 Жыл бұрын
100% not using ! or as, the amount of times I’ve seen bugs because people way up in the code create these assumptions and then I’m going through issues that are blatant lies to the transpiler
@eJuniorA2
@eJuniorA2 Жыл бұрын
By your logic the compiler is mistakenly misleading you by not throwing errors all around everytime you are using an Array or Record. Or by not forcing you handling throwed errors. Typescript has all kinds of hypocrite and conflicting assumptions all around wich we learn to handle. Sometimes you know MORE information than the compiler, you shouldnt throw away better knowledge "just in case you could be wrong" or for the sake of a "pattern". More knowledge is always better.
@jimbojones8713
@jimbojones8713 Жыл бұрын
so basically I used to do it correctly in just JS then with TS i got sloppy, and now im doing more boilerplate code in TS just for autocomplete
@Yutaro-Yoshii
@Yutaro-Yoshii Жыл бұрын
I am kind of annoyed that typescript doesn't complain about using literal index that might not exist on an array. If this is the case, then they should not complain about calling members.find with a non-existent id.
@BraedenSmith
@BraedenSmith Жыл бұрын
Agree this should be the default, but you can turn it on via noUncheckedIndexAccess.
@guillaumebrunerie
@guillaumebrunerie Жыл бұрын
It does, just use the --noUncheckedIndexedAccess option.
@purpinkn
@purpinkn Жыл бұрын
God that is ugly code.
@bbrother92
@bbrother92 Ай бұрын
what do you meant? his code? where I can find good code?
My Problem with Using TypeScript in 2023
8:15
James Q Quick
Рет қаралды 16 М.
40. Implement Intersection Types in the Typescript.
7:24
Leela Web Dev
Рет қаралды 2,2 М.
А ВЫ ЛЮБИТЕ ШКОЛУ?? #shorts
00:20
Паша Осадчий
Рет қаралды 7 МЛН
Building Fluent Interfaces in TypeScript
16:15
Andrew Burgess
Рет қаралды 15 М.
How to use generics in TypeScript
11:46
Andrew Burgess
Рет қаралды 36 М.
will i never understand this? unknown.
12:05
Andrew Burgess
Рет қаралды 3,6 М.
How does ZOD work? Build it yourself!
14:12
Andrew Burgess
Рет қаралды 12 М.
Interfaces vs Type Aliases: what's the difference?
10:20
Andrew Burgess
Рет қаралды 22 М.
old TypeScript syntax I just discovered
8:54
Andrew Burgess
Рет қаралды 4 М.
Generics: The most intimidating TypeScript feature
18:19
Matt Pocock
Рет қаралды 175 М.
any vs unknown vs never: TypeScript demystified
8:01
Andrew Burgess
Рет қаралды 23 М.
Why You Shouldn't Nest Your Code
8:30
CodeAesthetic
Рет қаралды 2,7 МЛН
TypeScript Generics are EASY once you know this
22:21
ByteGrad
Рет қаралды 136 М.