2 ways TypeScript LIES to you

Поділитися
Вставка
  • Опубліковано 3 січ 2025

КОМЕНТАРІ •

  • @asdqwe4427
    @asdqwe4427 Рік тому +57

    Ah, typescript. It's so... almost good

  • @codewithluke
    @codewithluke Рік тому +91

    Sounds like a case for Rust

  • @DeadAir
    @DeadAir Рік тому +29

    “Quack” - 🐢

  • @ex-xg5hh
    @ex-xg5hh Рік тому +6

    You actually can make an array invariant with 4.7 variance annotations
    interface InvariantArray extends Array {}

  • @georgespanos4680
    @georgespanos4680 Рік тому +112

    I've been doing front end with typescript for over 7 years now. What triggers me is that for large scale apps, I feel there is no alternative for quite some time. When I go and write something else like a cli tool, web api etc I always feel REJUVENATED by the plethora of choice. And when I want to write a UI, the programming world right now is like .I. to me.
    Edit: typo

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

      ReScript

    • @NathanHedglin
      @NathanHedglin Рік тому +6

      Purescript, Rescript

    • @nomoredarts8918
      @nomoredarts8918 Рік тому +6

      Elm, Rescript, Reason

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

      @@nomoredarts8918 Sadly ReasonML isn't for frontend anymore, I think

    • @georgespanos4680
      @georgespanos4680 Рік тому +11

      3/4 comments mentioned rescript. I was not familiar with it, I'm gonna give it a look. Thanks!

  • @FryuniGamer
    @FryuniGamer Рік тому +76

    I gave a internal presentation at work about the theorical background behind typescript and the glaring issues it causes. My favorite is creating a never from anything which destroys the type system entirely
    class Marker {}
    function castSilently(v: Marker): T {
    if (v instanceof Marker) throw null;
    return v;
    }
    The argument is reduced to never, but it actually accepts any value, not just instances of Marker

    • @corey4448
      @corey4448 Рік тому +4

      what, hows that even work??

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

      For me t's reduced to either unknown (if nothing is explicitly passed into T, or inferred from variable type), otherwise it's inferred.
      class Foo {} fits into class Bar {} - the name is irrelevant here. Why? because classes are not really anything! They are syntactic sugar over functions with prototype chains. You need to widen the type of the function by narrowing the type of its parameters.
      So it would be equivalent of doing function Foo() {} and function Bar() - both are functions, they accept the same parameters and return the same type, therefore they are equivalent.
      If that was not the case, then it would not be possible to pass functions around. It may seem counterintuitive at first, since you expect classes to behave differently. But this is JavaScript world :)

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

      That is absolutely intended behavior. The never type is the equivalent to the empty set in set theory, meaning anything is assignable to never.

    • @FryuniGamer
      @FryuniGamer Рік тому +13

      @@fallenpentagon1579 slightly incorrect.
      Never is indeed equivalent to the empty set, but that means that never is assignable to anything, not that anything is assignable to never.
      In fact, NOTHING is assignable to never, there is no value in the set, so no value should ever be assignable to it. Also, no set other than the empty set itself is a subset of the empty set so even without looking at the values it is clear that no other type is assignable to never while never is assignable to any type.
      So it is correct that a never type can be returned as T.
      The trick in this snippet happens because the assignability of the parameter uses structural typing and the instanceof check uses nominal typing, and they diverge in this case. So the parameter is a set of all values with that structure (which is all values), the conditional should narrow it to the set of instances of Marker, but since they are under the same name TS considers them to be the same. Since the conditional block is divergent (it throws) then TS assumes that the following statements run on the exclusion type, the difference of the sets, which is never.
      It is a wrong assumption in this case because of the structural and nominal mix, when it involves just one of them it is perfectly valid.
      But this IS the intended behavior.
      Edit: I sent a short answer before from my phone on the road, expanded it more now

  • @theaninova
    @theaninova Рік тому +10

    This is what made me go OH MY GOD FINALLY when I tried rust a few years back, immutable actually means immutable and you can't just start pushing items into const arrays.

  • @regorsears
    @regorsears Рік тому +30

    A weird one to me is that defining an interface/type method using the "method syntax" is not exactly the same as defining it using the "field + function type syntax":
    interface Foo { bar(n: number | string): void; } // Using method syntax, parameters are bivariant
    const foo: Foo = { bar: (x: string) => { /* ... */ } }
    foo.bar(10); // I just passed an integer to a function implementation that only takes a string.
    interface Foo2 { bar: (n: number | string) => void; } // Using field + function syntax, parameters are contravariant
    const foo2: Foo2 = { bar: (x: string) => { /* ... */ } } // Fails as "it should"

    • @ex-xg5hh
      @ex-xg5hh Рік тому +5

      This is the worst design decision they made so far. It was done mostly to keep arrays covariant, which is also bad. Before the introduction of strictFunctionTypes all parameter were bivariant. I hope they will add one more knob to turn this cringe off.

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

      interface Foo { bar(n: T): void } would solve your problem.
      (I agree with your point though)

  • @Fuzbo_
    @Fuzbo_ Рік тому +7

    While I think these examples are great, I don’t often (or ever) find myself or the people I work with mutating function arguments and *not returning the result.
    The only thing I could think of that might still break is if it’s a nested object and someone makes only a shallow copy, performs a mutation, returns the result, and as a by-product, accidentally mutated the function argument and uses that somewhere else.

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

      It's not likely to trip you up when you're consciously mutating it like in the case where you would return it.
      The scenario that induces hair loss is the one where a consumer of the object decides to mutate it somewhere without visibility. Now you're stuck with OOP shenanigans.

  • @michaeljmeyer3
    @michaeljmeyer3 Рік тому +11

    I did a presentation on some of these annoying quirks of TS called "Typescript is a liar.... sometimes" - a play off of one of the greatest 'Its Always Sunny in Philadelphia'. 'Science is a LIAR... sometimes'
    My previous work place loved it, my current one is.. well... yeah.
    If you make a full length video please do it in the format of the IASiP scene.

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

      let me guess, you work for microsoft now?

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

      @@wrong1029 oof no. I mean, I guess we all do. In the end...

    • @yeikiu
      @yeikiu 5 місяців тому +1

      @@michaeljmeyer3 been there. done that. Kudos bro!

  • @RuffyBankai
    @RuffyBankai Рік тому +5

    My disappointment at the lack of coconut oil in this video is immeasurable and my day is ruined.

  • @MrBaudin
    @MrBaudin Рік тому +9

    Another example which really hurts my brain; you can return a completely different object from a constructor. Really painful

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

      Well yeah, that's how functions work in JS. Classes are just fancy syntactic sugar functions with prototypes. The concept of "constructor" is just using "new" in front of a function call to create a new prototype instance.

    • @MrBaudin
      @MrBaudin Рік тому +4

      I understand why it happened, it’s why I still resist using the class keyword. Just use functions, use the module to hide private things.

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

      @@MrBaudin Agreed!

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

      We dont even have async constructors :,(

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

    btw, about "E492: Not an editor command: W". You can bind :W command to :w (and also :WQ and :Wq to :wq). It really hapls me a lot...

  • @philyoosays
    @philyoosays Рік тому +11

    If you don't enable "downlevelIteration" in your tsconfig then TS will not let you splat Set objects as if they were arrays. It causes inconsistent behavior esp if you end up calling a JS file that splats a Set because running it with node will work but running it with ts-node will cause the splat to be undefined (if downlevelIteration is not true).

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

      This is because downlevelIteration is a compatibility feature to support splatting in ES3 and ES5. If you want to support "splatting" natively, you should target any ECMAScript higher than ES5. You have to make sure the runtime you run in also supports that feature (ES6+)

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

    we need a compilation in the NotNorm macdonald channel: Constant shitting on typescript, but typescript is a good guy. 🤣🤣

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

    To be fair, in the first example its explicitly an (array of [number or string]) not an ([array of number] or [array of string]), so it checks out that adding a number to an array of strings results in an array where the elements are only number or string
    Yes, still an easy footgun, but in this case its just setting the wrong type and it doing what's expected of that type rather than doing everything right and encountering unexpected behavior

    • @VazhaAsatiani
      @VazhaAsatiani 7 місяців тому

      yep. just write better types

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

    You can also access an element in typed array like this arr[i] - and it does not recognize, that this can return undefined. While it works fine in for loops where you limit the range of i, in other places you can get a runtime error like that.

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

    you can use nominal types, though it will be a bit clunky
    type ReadOnlyResult = {
    readonly hello : string
    } & {description: "readOnly"}
    type Result = {
    hello : string
    }
    const toReadOnly = (item) :ReadOnlyResult => item as ReadOnlyResult
    const test = {hello:'world'}
    const typedTest = toReadOnly(item)
    const mutateResult = (item:Result) => {
    item.hello = 'barf'
    }
    // error
    mutateResult(typedTest)

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

    The read-only thing is so strange..
    Since js does have a way to make properties read only that you'd think it would transpile to...

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

      My most hated feature. Tons of type matching P.I.T.A. for literally zero benefits. And you cant turn it off (as const always produces readonly)

  • @idiota-w-swiecie
    @idiota-w-swiecie Рік тому +2

    Man typescript at least give you like datatypes like string or number, imagine c where everything is a f*king bytes and the compiler doesn't give a sh*t. In some implementations via pointers you can even MUTATE f*king CONSTANTS

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

    Honestly, this is a good case for a language called Rescript which is Javascript with OCaml’s type system strapped to it. While I intend to stick with Elm and Mint on the frontend, Rescript is just Typescript but good by breaking backwards compatibility.

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

    Assign a method of a class to a property of another class and try to call it. The value for `this` will get changed when it's assigned so it won't work. I solved a bug caused by this like 5 minutes ago.

  • @diadetediotedio6918
    @diadetediotedio6918 Рік тому +9

    This could be solved by typescript being more strict about what it consideer a duck
    For me it is clear that a (number | string)[] is structurally different from a string[], and that a readonly T is different from just T, if they could just handle that taking in account the modifiers as part of the structure itself of the type it would solve this problem.

    • @DryBones111
      @DryBones111 Рік тому +4

      It seems like "structural typing" is really just Microsoft speak for "duck typing at compile time".

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

    ohh yeah. More footgun unlocked🍻

  • @MrMudbill
    @MrMudbill Рік тому +47

    Another array lie is when you just take an index, like `const item = items[n];`
    item will become a type of whatever that array is, without considering that it might be undefined if the index is out of bounds. I know this is deliberate, but still risky.

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

      Yeah, you'd need an option type in javascript to fix that. Probably not happening 😕

    • @9SMTM6
      @9SMTM6 Рік тому +17

      -Primeagens example is fixed with strictFunctionTypes- , yours is fixed with noUncheckedIndexedAccess.
      *EDIT:* Been wrong about Primagens example. I'm still fairly confident in the solution to your issue 😅

    • @SamualN
      @SamualN Рік тому +12

      @@asdqwe4427 typescript *does* have an option type, it's called `T | undefined`

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

      @@SamualN that doesn’t count lol

    • @fallenpentagon1579
      @fallenpentagon1579 Рік тому +20

      There is the "noUncheckedIndexAccess" flag which fixes this behavior

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

    This being the backbone of my everyday stack brings me nightmares

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

    This is why we have languages like Haskell with real type systems.

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

      Yeah most of the good stuff in rusts types is heavily inspired from ML and Haskell

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

    Enums in TS suck. Sometimes it only accepts the key, sometimes only the value. Sometimes what is typed as a key is really a value at runtime. Not sure if this counts as lying. Sorry can't remember the specifics just thdt this really bit me

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

    One must be clear that typeScript is only statically checking the code at compile time and does not replace proper runtime checks and error handling.

    • @alexaka1
      @alexaka1 Рік тому +5

      Having a strongly typed language ensures that you don't have to write runtime checks since the typing forces you to write code that doesn't violate the strict types. This is what TypeScript is supposed to be, but at this point why even have it? If I have to write stupid runtime checks in a strongly typed language, something has gone horribly wrong. Imagine writing runtime checks in C to make sure an int is actually not a char[] or a long long. That would be the stupidest thing ever.

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

      Because typescript is usually dealing with web-based platforms? You can’t always guarantee what kind of data an API may return especially if you don’t own it.

  • @simonlundberg8194
    @simonlundberg8194 Рік тому +4

    Late to this one but whatever. Here's one of my most hated aspects of TS: implicit interface conformance.
    type Vec2 = {x: number, y: number}
    type Vec3 = {x: number, y: number, z: number}
    function dot2d(a: Vec2, b: Vec2): number {
    return a.x * b.x + a.y * b.y
    }
    const a: Vec3 = {...}
    const b: Vec3 = {...}
    dot2d(a, b) // In any normal language, this won't compile
    Who the hell thought this was a good idea?

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

      quack quack

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

      @@ThePrimeTimeagen If I wanted duck typing with JavaScript, I'd just use JavaScript. ¯\_(ツ)_/¯
      I'd pick TS over JS any day of the week but man this "type safe (haha, you thought)" approach is pretty annoying.

  • @igniuss
    @igniuss Рік тому +53

    ReadOnly and privates are just suggestions, including in other languages like C# 😂

    • @Bliss467
      @Bliss467 Рік тому +11

      C# doesn’t compile without reflection shenanigans if you want to mutate read only fields that aren’t visible

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

      @@Bliss467 but reflection shenanigans are easy as pie 👀

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

      ​@@igniuss not really

    • @victor1882
      @victor1882 Рік тому +5

      ​@@igniuss but they're explicit, you meant to do that and it's not some hidden behavior

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

      @@igniuss pretty sure if it is a readonly field you can't set it with reflection either. Sure you can set properties with a private setter but if it is actually readonly then I think you'd have to resort to some unsafe code to get at the memory if you wanted to change it.

  • @nescafezos4265
    @nescafezos4265 17 годин тому

    I jnew "or" types was a mistake in a langauge that tries to be strongly-typed. C# for example does not have such concept of "|" types. very great example that shows this!

  • @dealloc
    @dealloc Рік тому +9

    Well it makes sense as TypeScript isn't smarter than your types, really. TypeScript can only see what input and output you have. What it should have is a way to declare something as mutating, then restrict the types even further (e.g. not allow to fit string[] into (string | number)[] and readonly into non-readonly equivalent types)
    I'm sure there have been proposals for that.

    • @sheep4483
      @sheep4483 Рік тому +4

      Well, I don't use TS, but from this it seems like TS is not only not smarter than your types, but is actively dumber than your types. Allowing a string[] to be treated as a (string | number)[] seems exceptionally weird and like a terrible choice, but at least somewhat understandable, but allowing what appear to be two completely unrelated types to be treated the same is utterly baffling. What logic does it even follow to conclude that a ReadonlyResult could possibly be allowed to be passed to a function that takes a Result? Because they're both "Objects?" Because their fields have the same names?

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

      @@sheep4483 Unions are not a TS-only concept. Union is the same as "OR" in bitwise operators-in fact it's also known as a bitwise union. This is why they use the same syntax "|" (pipe) to indicate this.
      So it makes perfectly sense when you pass a string into a type of (string | number). In this case you're telling it, I can store any value in the array that is either a string or a number. Not as a whole.
      This would be different from string[] | number[] which would mean, either an array of strings OR an array of numbers. Either way in this case it wouldn't matter, because he is passing a string[] so both those types would fit.
      The problem here is not the types themselves, it's that TypeScript has no way to guard against mutations in its type-system.

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

      ​@@dealloc ​ Yes, unions are not a TS-only concept, however the problem is that TS unions do not work the same as unions in the majority of languages that have them.
      For example, if you had a union StringOrNumber in C, and you have a function that takes that type, you are unable to pass a string or a number into that function, you must give it the union type that it wants, not one or the other. They're entirely distinct, so you must first explicitly convert it.
      I would say that it makes sense to allow simply interpreting a scalar value as this union type without any explicit conversion in TS, however it's clearly a bad idea for non-scalar values where the value could be mutated.
      Typescript *could* handle that by giving additional information to the type system, but I would argue it should also first handle that by simply not allowing you to shoot yourself in the foot with the currently existing type system, which I think is what the primary goal of using TS over JS is in the first place.
      But that still doesn't explain why a ReadonlyResult could ever reasonably be interpreted as Result, could it also interpret a Square as a Triangle?

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

      @@sheep4483 The difference between TypeScript unions and C unions is that C unions are not sum types, whereas TypeScript unions are.
      Sum types are coproducts of types, meaning they represent either, or both types. C unions doesn't, but also does not prevent you from represent a union U as either a string or a number as they are not type-safe in C. C++ has a std::variant that is a sum type, but are tagged, unlike TypeScript's unions which are untagged (non-discriminated) by default.
      You can represent tagged unions/descriminated unions in TypeScript by adding additional information, like a label to a type: `{ kind: StringTag, ... } | { kind: NumberTag }`.
      I agree that it's not ideal that some types shouldn't be compatible-for example readonly vs non-readonly object of the same type. It seems odd to add a `readonly` modifier if it doesn't mean anything in case of objects. However, there are array types that does work as intended; ReadonlyArray does not fit into to mutable Array type, but Array does fit into ReadonlyArray since you cannot mutate ReadonlyArray.

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

    That second one really looks like a bug, and not a feature, TBH

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

    Union types as array items are a total mess overall imo

  • @Sam-wl4vo
    @Sam-wl4vo Рік тому

    you are such a good entertainer and an even better teacher

  • @coding_with_chip
    @coding_with_chip Рік тому +11

    you should be a QA dev for typescript.. this is a huge bug that made it into production

    • @xshady2967
      @xshady2967 Рік тому +7

      typescript is unsound and it's known

    • @ex-xg5hh
      @ex-xg5hh Рік тому +4

      It's not a bug, but a horrible design decision made on purpose. They justify this by saying that large part of the ecosystem relies on this behavior being possible. Why they haven't just put that in the compiler settings is a mystery.

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

    You know when you run ts code, all type information disappears?

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

      Well technically you don't run TS code, you run JS code.

  • @stele9164
    @stele9164 Рік тому +5

    You can pass any type variable to a method even if it isn't the specified type to be passed. The other day at work i wrote a method that called (var: number) and did some checks on var, in the else statement, returned var. I did not realize that sometimes var would be passed as a string, ran it, and nothing broke. I was pretty confused when I looked back over.

  • @wadecodez
    @wadecodez Рік тому +4

    And typescript is transpiled so this could totally be fixed but they won’t fix it. So tempting to write a better type safe language for JS

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

      It exists: purescript

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

      @@belst_ lol no. anything but ts deviates too far from the base syntax. makes it hard to adopt on existing projects.

    • @guillemgarcia3630
      @guillemgarcia3630 Рік тому +5

      @@wadecodez I wish I could say rescript :/

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

    (string[])[0] can be undefined

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

    You can't spread arguments into a constructor function.
    Ex: contructor(x:number, y: number)
    Ex: array = [0, 0]
    Ex: new YourClassName(...array)
    Problem: I encountered this in typescript, but thinking about it I'm almost positive regular JS throws an error too

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

      Why would you want to do this?

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

      @@eqprog spread an array into a constructor? It's effectively the main thing parsers do; reading structure into 2d sequence and lifting it out into a 3d structure. you wouldn't do this particular example.

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

    Can you guys tell me, what colorsheme is it?🔥

  • @JacobChristiansen1
    @JacobChristiansen1 6 місяців тому

    in the first case your type should have been `string[] | number[]`

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

    3:00
    Missing "as const" ?

  • @3ombieautopilot
    @3ombieautopilot Рік тому

    And how do we help this situation? Like how do you make readonly fields in TS?

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

    Is this related at all to covariance and contravariance?

  • @lpanebr
    @lpanebr Рік тому +5

    You're going to piss Teo. I like it!

  • @BlurryBit
    @BlurryBit Рік тому +4

    To be fair JS is ugly. TS ultimately compiles to JS unless someone is doing something cool again..
    As for the non mutable objects, I personally do something like:
    const nonMutableObject = {
    name: "Bob",
    age: 69
    } as const;
    I know it looks funny with those two consts, but it seems to work for me. The only problem is that you can't do a partially mutable object with this by specifying readonly to a property.
    I would love the code above to be taken apart if possible. That way we all learn something lol. :D

    • @dealloc
      @dealloc Рік тому +5

      As const is just syntactic sugar for making a constant object with readonly properties. You can still mutate it technically:
      For example:
      function produceReadonlyResult() {
      return {
      foo: 69,
      bar: 420,
      } as const;
      }
      const item = produceReadonlyResult();
      function mutateResult(result: Result) {
      result.foo += 1337;
      result.bar += 1337;
      }
      mutateResult(item);

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

    You cane make same thing with "any" type argument:
    function mutate(a: any){
    a.someProperty = "foo";
    delete a.secondProperty;
    }
    function add(arr: any[]){
    arr.push("Something");
    }

  • @tilakmadichettitheappdeveloper

    Okay...but what's the solution ? Use JSDocs ????

    • @ThePrimeTimeagen
      @ThePrimeTimeagen  Рік тому +15

      no, there is no solution, you have to literally avoid these issues.

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

      Soulsucking way of at least getting it caught during runtime is to freeze the object and it's properties

    • @9SMTM6
      @9SMTM6 Рік тому +2

      @@ThePrimeTimeagen-that is wrong.-
      *EDIT* In fact I'm in the wrong here, as nicely pointed out nicely by lalith below. I'll leave this for posterity.
      The bivariant parameter and return type issue you were showing is solved with the strictFunctionTypes Flag. This makes parameters contravariant and return covariant, as is correct.
      Which any competent tooling will enable when generating templates etc btw.
      It ain't perfect but it's perfectly solvable.
      Perhaps there's ways around it, but I've never stumbled upon them in my projects.
      Please consult with people that know a technology before making claims such as that one. The very same behavior is what's holding back Rust.

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

      Use generics instead and specify the return type explicitly.
      function mutateArray(items: T[], item: T): void {
      items.push(item);
      }
      // This works
      console.log(mutateArray([1, 2], 3));
      console.log(mutateArray(['str1', 'str2'], 'str3'));
      // This gives an error
      console.log(mutateArray([1, 2], '3'));
      console.log(mutateArray([1, '2'], 3));
      console.log(mutateArray(['str1', 'str2'], 3));
      console.log(mutateArray(['str1', 2], 'str3'));

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

      @@9SMTM6 Oh.. really. Then explain why this is compiling on TS playground and then throwing run time error. You can check on tsconfig that strictFunctionTypes is on (by default).
      What exactly did this flag solved? I shall be waiting here for my answer.
      function mutateArray(items: (number | string)[]) {
      items.push(69);
      }
      const items: string[] = ["hello", "world"];
      mutateArray(items);
      console.log(items);
      function doSomething(items: string[]) {
      items.forEach(x => console.log(`${x}: ${x.split('')}`));
      }
      doSomething(items);

  • @vaibhavsinghtomar4585
    @vaibhavsinghtomar4585 5 місяців тому

    How does you switches between terminal and the nvim, so smoothly ?

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

    ts stands for trashscript

  • @angelcaru
    @angelcaru 9 місяців тому +2

    IT'S NOT A DUCK

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

    I was working on a project and I found the following type used everywhere:
    ```
    interface CustomType {
    [key:string]: any;
    }
    ```
    This is just obfuscation for using `:any`. Then I also found this:
    ```
    class X implements X {
    }
    ```

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

      just run

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

      It is not just an obfuscation for any. You cannot say for e.g.
      const x: CustomType = 5;

  • @muhammadsalmanafzal9396
    @muhammadsalmanafzal9396 Рік тому +7

    if you use strict type checking, the compiler tells you to explicitly check type which could remove this vagueness.

    • @dealloc
      @dealloc Рік тому +9

      This is strict mode.

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

      Yeah, these protections exist for arrays - a readonly array is treated as more specific than a mutable array. That makes sense - you can treat any mutable array as readonly; just don't call the mutable methods. But you can't do the opposite - if you try calling a mutable method on a readonly array, TypeScript will (rightly) yell at you.
      The problem is that the protections don't exist for objects. Even if you use the Readonly utility type, that doesn't stop the same error Prime showed from happening. All TypeScript objects are structurally-typed, and a mutable/immutable objects are going to be treated as interchangeable, even with the strictest compiler settings. Maybe that can change in the future with some new compiler setting, but we don't have that luxury right now, even with TypeScript 5 on the horizon.

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

    As a C++ (rust --) developer this hurts my brain. why

  • @nomoredarts8918
    @nomoredarts8918 Рік тому +6

    It's a language level feature, typescript cannot do this

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

      Would be funny if TS was a language. But all good.

  • @muzam1l
    @muzam1l Рік тому +6

    Wtf! First example defines items as `(number | string)[]` which literarily says the array can contain strings or numbers. The intended behavior would come from `string[] | number[]` 🤷

    • @nathaaaaaa
      @nathaaaaaa 3 місяці тому +1

      The point is that the function made a string[] have a number, but compile time still thinks it's a string[].

  • @vitiok78
    @vitiok78 Рік тому +6

    Typescript is just a very advanced documentation tool for your JavaScript. Nothing more.
    I sometimes think that a good IDE + JSDoc is a better approach because you don't have false expectations...

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

      There is that but this makes perfect sense when you think about what difference between type and interface/class.
      The type with read only in it fits in perfectly with Result. It all matches.

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

    Second example is not the best, as Result and ReadonlyResult have same properties and you will not produce an error passing a ReadonlyResult to that function instead of a Result. And THAT is the matter. Try passing a ReadonlyResult instead...

  • @H4K.
    @H4K. Рік тому

    How do you get neiovim to be centered like that rather than to the left?

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

    How fucking fast do you type holy shit xD. I have been practicing my typing a lot and was pretty proud of 97 wpm, but I feel like you're at 140 or higher: insane.
    Anyway I feel like a real idiot because I thought it might be kind of nice when I was learning JavaScript (and TypeScript) that something like this would be a good thing. That in hindsight was pretty stupid of me. Love this video I would like to see more videos of you coding!

  • @Blast-Forward
    @Blast-Forward Рік тому

    Is this really with all strict compiler options on?

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

    wait so what does the “readonly” keyword do?

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

      oh it’s an implicit conversion from the parameter declaration, still horrible, but at least direct consumers can’t modify it - it’s kind of like a PleaseReadOnly type and to do it right in front of the object’s face is just rude, but if you pass it to the mutator, it’s like “hey, I didn’t change it, it was that function, take it up with him”

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

    what's the ide/code editor here? (newbie question)

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

    Time to get the Elm train going.

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

      elm, purescript, fable - this is the way!

    • @asdqwe4427
      @asdqwe4427 Рік тому +5

      nah, it's better to wait for wasm 🦀

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

    i'm sorry what's the name?

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

    I really don't get ts. If JavaScript type system isn't adequate for your project then maybe js isn't the right tool for your project. One wouldn't use a screwdriver to unscrew a bolt, nor a wrench to unscrew a screw. Both wrench and screwdriver "unscrew" things but doesn't mean they are replaceable. DOM manipulation? Js
    Backend development? Almost anything else

  • @kech-agmaio8620
    @kech-agmaio8620 Рік тому +1

    lovin the vim lol

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

    Only per default (I think there is a setting for it to not behave like that):
    const array: string[] = [];
    function doSometing(str: string) {
    return str.replace("a", "b");
    }
    console.log(doSometing(array[0]));
    No type error but will crash because undefined has no property replace.

  • @nullignore99
    @nullignore99 Рік тому +13

    My main issue with the first example is that within the scope of mutateArray, TS only understands the variable "items" based on type provided in the function signature. If you wanted it to understand it based on the value passed, you should have used generics. I'm not saying TS is perfect. I just think that you should have only talked about the second example since that is a valid one.

  • @Ahmed-S-Lilah
    @Ahmed-S-Lilah Рік тому +4

    I am curious to see you react to "Object-oriented Programming bad" and "Object-oriented Programming is garbage" by Brian Will it's just amazing 👏
    he also made a video named "Object-oriented Programming is good*"

  • @Pavel-wj7gy
    @Pavel-wj7gy Рік тому

    Can anyone show an equivalent example of how Rust solves this problem? (I'm not writing in Rust but I'm really curious).

    • @dealloc
      @dealloc Рік тому +5

      You can't really with arbitrary type unions (e.g. string | number). Rust doesn't have types like these, because it doesn't need them unlike TypeScript which needs to represent the dynamic types of JavaScript.
      In Rust you would often use an enum to represent a value that can represent different kinds of values:
      enum Value {
      String(String)
      Number(f64),
      }
      In Rust you also have to explicitly mark something as mutable if you want to mutate it. You will also be nagged by the borrow checker to pass mutable references to functions that mutate its value. This is to save you against a lot of bugs and memory safety issues.
      In Rust references and non-reference is part of the type system in a way. For example &mut T is not the same as &T, nor is mut T or T by itself. They all add constraints to how you can use said value in your code. This is a selling point of the borrow checker in Rust.
      If Rust had issues with what the video above shows, then it would be a bug in the type system, and you should file an issue on their repository.

    • @Pavel-wj7gy
      @Pavel-wj7gy Рік тому

      @@dealloc Thank you! I'm going to save your answer for future reference, if you don't mind.

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

    What happened?

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

    Why we're taking too many roundtrips to achieve safety, why not just using Rust

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

    Can't you use "As const" ?

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

    I'm a scrub so this is a genuine question, but doesn't a union type defeat the purpose of a type system? Like why is that there?

    • @MrMudbill
      @MrMudbill Рік тому +5

      The lack of a type system would allow any type; a union of everything. Sometimes you want only a union of _some_ things. It still protects you against the rest of types (well, in theory).

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

      @@MrMudbill I get the point of types in general and why they're good, but the thought of letting more than 1 type through seems like an oxymoron. Again, scrub so I don't know what I don't know. Thank you though

    • @LeoVital
      @LeoVital Рік тому +4

      Bear in mind that TS transpiles to JS, and JS is weird. For example, maybe you want to explicitly state that the type of something can be null, so you'd use the union type for that.
      But overall, yeah, I prefer stronger type systems.

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

      @@datguy4104 I like such unions and I don’t think it defeats the purpose of types but it sure as hell makes it more complex so TypeScript better didn’t do this since it has so many stuff it doesn’t do it actually should do. TypeScript is a linter a script tool nothing more.

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

      @@LeoVital allowing null made it make sense to me

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

    In the second one, since item contains the reference to the object and not the object itself, hence a mutability to the object is allowed but no error is thrown since the reference is still remains the same

  • @anubhavseet004
    @anubhavseet004 6 місяців тому

    so you are saying typescript is not typescript at all

  • @Techgeek002
    @Techgeek002 6 місяців тому

    Maybe use brand

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

    This would never happen in rust :p

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

    Where are the solutions for these

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

    Me watching your TS videos makes me love that I don't write TS.

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

    I wonder if TS could work around that with primitive wrappers and Object.seal() and Object.freeze()

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

    I just don't understand why the function is ok accepting a type that just happens to have the same key type pairs. There's nothing defined to bind the two types together so why does TS think they're the same?

    • @DubiousNachos
      @DubiousNachos Рік тому +5

      TypeScript is structurally-typed instead of nominally-typed, and structural typing is just a slightly fancier version of duck-typing. If you satisfy the basis structure that a function is looking for, that's good enough for TypeScript.

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

    weed onwee pweese 🥺 👉👈

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

    avoid mutations like the plague!!

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

    This is fixed by using TS enums instead, because those do not get duck-typed. Still, using the enums is tricky and adds complexity because you might end up needing the enum as a field in your object to discriminate. (They are not nice like Rust enums)

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

    In the first example I literally see nothing wrong?

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

      You declared items is an array of strings, then you mutated it, adding a number, now it's not an array of strings anymore.

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

      You can run that example on TS playground and see for yourself. Here I typed it out for your lazy ass. Can't share link as automod removes them. I hope copy-paste isn't too much for you.
      function mutateArray(items: (number | string)[]) {
      items.push(69);
      }
      const items: string[] = ["hello", "world"];
      mutateArray(items);
      console.log(items);
      function doSomething(items: string[]) {
      items.forEach(x => console.log(`${x}: ${x.split('')}`));
      }
      doSomething(items);

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

      @@lalit5408 Wait, but you said "Mutate an array which is number or string array" so technically its fine

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

    at this point just make web servers in java or rust

  • @jeffreyblack666
    @jeffreyblack666 11 місяців тому

    I haven't been using typescript as I think it is a steaming pile of crap and a complete waste and don't fully understand how it works. But I assume that internally they are just using JS objects, and checking that the type matches when it is called, not when it is being used?
    The hilarious thing is you can actually get this type safety in regular JavaScript.
    You can define a class for any particular type and set it up in a way to get these results.
    e.g. for the first you can create a class, extending the array class, and have things which add to the array or mutate values in the array check the type before allowing it.
    Likewise you can define properties on objects which are readonly.
    The fact that typescript doesn't handle that and doesn't transpile to do that is hilarious and just shows how useless typescript is.

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

    first example can even happen for statically typed language like c#
    void updateArray(object[] a) { a[0] = new object(); }
    string[] x = new string[10];
    updateArray(x);
    System.ArrayTypeMismatchException: 'Attempted to access an element as a type incompatible with the array.'
    To fix these type of things array should be converted to "readonly" when downcasted as argument

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

    Wouldn't the first example be mitigated by defining the return type for the function? If you typed it as returning string[], then items.push(69) would throw an error. This could be extended further using genetics, though maybe I'm missing the point.
    Edit: At first I thought it was returning a new array, and not mutating the args. That's my bad.

    • @9SMTM6
      @9SMTM6 Рік тому +3

      He's not returning an array, he's just manipulating it. It is in fact a type error, and there is no holistic solution, differently to what I've claimed elsewhere.

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

      @@9SMTM6 ah yeah I see that now. Fair enough.

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

    Bro wtf. Typescript is just as dynamic as JS. Why tf do I need TS if i can blow types with JS itself.

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

    You love your mutations don't you? 😉

  • @tokiomutex4148
    @tokiomutex4148 Рік тому +9

    Structural typing sucks

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

      it does

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

      On its own it has problems but doesn't suck that much
      The horrendous mix of structural typing and nominal typing in TypeScript definitely sucks

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

      I don't think this is a problem specific to structural typing

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

      It does not suck, it ducks.

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

      @@michaelbitzer7295 lmao

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

    May I know the reason for choosing the numbers as 69 and 420?

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

    The array example more looks like a Python list. You can put whatever items in there you want.
    The second problem seems inexcusable to me. If 'item' is passed by reference, the function should not be able to mutate it.

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

    Js ❤