The TypeScript feature I never use

Поділитися
Вставка
  • Опубліковано 2 кві 2023
  • 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
  • Наука та технологія

КОМЕНТАРІ • 66

  • @StephenMoreira
    @StephenMoreira Рік тому

    Thanks for your thoughts. Always love hearing the fundamental perspective of other developers.

  • @pastenml
    @pastenml Рік тому +16

    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 Рік тому +8

      I've found validating those 'unknown' types to be a good way to handle those cases, with something like zod

    • @doremicocoparis9410
      @doremicocoparis9410 Рік тому +2

      Yeah I use zod for this, trust but verify

    • @necrifed
      @necrifed 8 місяців тому

      Everyday I hate typescript more 😂

  • @RobertKonigsberg
    @RobertKonigsberg Рік тому

    `asserts arg is Type` is new to me. Very nice.

  •  Рік тому +10

    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 Рік тому +1

      To check for undefined wouldn't you want `== null` instead?

    • @andrew-burgess
      @andrew-burgess  Рік тому +1

      yep, `t == null` would definitely be a better way to do this, good callout!

    • @kbsanders
      @kbsanders Рік тому +1

      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.

  • @Guergeiro
    @Guergeiro Рік тому +8

    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 Рік тому +2

      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 Рік тому +3

      "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?

  • @tzuilee588
    @tzuilee588 Рік тому

    Your explanation is so good! 😁

  • @meyou118
    @meyou118 Рік тому +2

    good advise - thx

  • @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.

  • @ness-ee
    @ness-ee 8 місяців тому

    Another one of your videos that I’ll show to the team.

  • @magicbob8
    @magicbob8 Рік тому

    This is a great video!

  • @oscarcastillejo9685
    @oscarcastillejo9685 Рік тому +3

    Another way to see the “as const” is to declare a literal

  • @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.

  • @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.

  • @ricko13
    @ricko13 Рік тому +3

    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 Рік тому +1

      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!

  • @wtl912
    @wtl912 Місяць тому

    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!

  • @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

  • @Azoraqua
    @Azoraqua Рік тому +2

    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 = (...) {...}`

  • @najlepszyinformatyk1661
    @najlepszyinformatyk1661 Рік тому +1

    What is the difference between just const and declare const?

  • @pawekoaczynski4505
    @pawekoaczynski4505 Рік тому +1

    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

  • @KirkWaiblinger
    @KirkWaiblinger Рік тому +1

    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)

  • @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.

  • @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` !

  • @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.

  • @camotubi
    @camotubi Рік тому

    Me yolo swaging with "as unknown as T"

  • @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.

  • @artist6000ish
    @artist6000ish 9 місяців тому

    Casting comes from 'C. It's not a runtime behavior. idk where you get that casting is a runtime behavior.

  • @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.

  • @yfzhangphonn
    @yfzhangphonn Рік тому

    That's when you need Functional programming. ^^

  • @amans6504
    @amans6504 Рік тому

    We eventually need rutime checks at many such places

  • @mk72v2oq
    @mk72v2oq Рік тому

    Enable "noUncheckedIndexedAccess" compiler option for your codebase and enter a whole new world xD

  • @FunctionGermany
    @FunctionGermany Рік тому +2

    9:00 zod

  • @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 8 місяців тому

      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

  • @eJuniorA2
    @eJuniorA2 10 місяців тому

    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

  • @hugodsa89
    @hugodsa89 Рік тому +1

    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

  • @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.

  • @tahasoft1
    @tahasoft1 10 місяців тому +1

    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)

  • @purpinkn
    @purpinkn Рік тому +3

    God that is ugly code.