Тёмный

2 ways TypeScript LIES to you 

ThePrimeTime
Подписаться 474 тыс.
Просмотров 44 тыс.
50% 1

Recorded live on twitch, GET IN
/ theprimeagen
MY MAIN YT CHANNEL: Has well edited engineering videos
/ theprimeagen
Discord
/ discord

Наука

Опубликовано:

 

4 мар 2023

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 266   
@asdqwe4427
@asdqwe4427 Год назад
Ah, typescript. It's so... almost good
@codewithluke
@codewithluke Год назад
Sounds like a case for Rust
@ThePrimeTimeagen
@ThePrimeTimeagen Год назад
yerp
@tejeshreddy6252
@tejeshreddy6252 Год назад
What isn't!?
@RicardoValero95
@RicardoValero95 Год назад
Or @effect/data
@DeadAir
@DeadAir Год назад
“Quack” - 🐢
@MrTyty527
@MrTyty527 Год назад
lmaooo
@michaeljmeyer3
@michaeljmeyer3 Год назад
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 Год назад
let me guess, you work for microsoft now?
@michaeljmeyer3
@michaeljmeyer3 Год назад
@@wrong1029 oof no. I mean, I guess we all do. In the end...
@ex-xg5hh
@ex-xg5hh Год назад
You actually can make an array invariant with 4.7 variance annotations interface InvariantArray extends Array {}
@regorsears
@regorsears Год назад
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 Год назад
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 10 месяцев назад
interface Foo { bar(n: T): void } would solve your problem. (I agree with your point though)
@RuffyBankai
@RuffyBankai Год назад
My disappointment at the lack of coconut oil in this video is immeasurable and my day is ruined.
@georgespanos4680
@georgespanos4680 Год назад
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 Год назад
ReScript
@NathanHedglin
@NathanHedglin Год назад
Purescript, Rescript
@nomoredarts8918
@nomoredarts8918 Год назад
Elm, Rescript, Reason
@zzzyyyxxx
@zzzyyyxxx Год назад
@@nomoredarts8918 Sadly ReasonML isn't for frontend anymore, I think
@georgespanos4680
@georgespanos4680 Год назад
3/4 comments mentioned rescript. I was not familiar with it, I'm gonna give it a look. Thanks!
@Sam-wl4vo
@Sam-wl4vo Год назад
you are such a good entertainer and an even better teacher
@FryuniGamer
@FryuniGamer Год назад
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 Год назад
what, hows that even work??
@dealloc
@dealloc Год назад
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 Год назад
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 Год назад
@@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 Год назад
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.
@theshermantanker7043
@theshermantanker7043 4 месяца назад
Same goes for C++
@Fuzbo_
@Fuzbo_ Год назад
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.
@MrBaudin
@MrBaudin Год назад
Another example which really hurts my brain; you can return a completely different object from a constructor. Really painful
@dealloc
@dealloc Год назад
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 Год назад
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 :,(
@philyoosays
@philyoosays Год назад
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 Год назад
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 Год назад
we need a compilation in the NotNorm macdonald channel: Constant shitting on typescript, but typescript is a good guy. 🤣🤣
@xoutaku7600
@xoutaku7600 Год назад
This being the backbone of my everyday stack brings me nightmares
@hary6416
@hary6416 Год назад
ohh yeah. More footgun unlocked🍻
@serversender9919
@serversender9919 Год назад
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 Год назад
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 10 месяцев назад
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)
@esper_lvl4900
@esper_lvl4900 Месяц назад
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.
@blucky_yt
@blucky_yt 10 месяцев назад
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 Месяц назад
yep. just write better types
@tigranrostomyan9231
@tigranrostomyan9231 8 месяцев назад
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...
@kech-agmaio8620
@kech-agmaio8620 Год назад
lovin the vim lol
@H4K.
@H4K. Год назад
How do you get neiovim to be centered like that rather than to the left?
@enderger5308
@enderger5308 4 месяца назад
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.
@JacobChristiansen1
@JacobChristiansen1 7 дней назад
in the first case your type should have been `string[] | number[]`
@MarcelRobitaille
@MarcelRobitaille Год назад
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
@angelcaru
@angelcaru 3 месяца назад
IT'S NOT A DUCK
@3ombieautopilot
@3ombieautopilot 10 месяцев назад
And how do we help this situation? Like how do you make readonly fields in TS?
@igniuss
@igniuss Год назад
ReadOnly and privates are just suggestions, including in other languages like C# 😂
@Bliss467
@Bliss467 Год назад
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 Год назад
​@@igniuss but they're explicit, you meant to do that and it's not some hidden behavior
@calebvear7381
@calebvear7381 Год назад
@@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.
@xendergo4528
@xendergo4528 Год назад
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.
@MrMudbill
@MrMudbill Год назад
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 Год назад
Yeah, you'd need an option type in javascript to fix that. Probably not happening 😕
@9SMTM6
@9SMTM6 Год назад
-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 Год назад
@@asdqwe4427 typescript *does* have an option type, it's called `T | undefined`
@asdqwe4427
@asdqwe4427 Год назад
@@SamualN that doesn’t count lol
@fallenpentagon1579
@fallenpentagon1579 Год назад
There is the "noUncheckedIndexAccess" flag which fixes this behavior
@diadetediotedio6918
@diadetediotedio6918 Год назад
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 Год назад
It seems like "structural typing" is really just Microsoft speak for "duck typing at compile time".
@PlerbyMcFlerb
@PlerbyMcFlerb Год назад
Is this related at all to covariance and contravariance?
@MadaraUchihaSecondRikudo
@MadaraUchihaSecondRikudo Год назад
That second one really looks like a bug, and not a feature, TBH
@acelga
@acelga Год назад
i'm sorry what's the name?
@simonlundberg8194
@simonlundberg8194 Год назад
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 Год назад
quack quack
@simonlundberg8194
@simonlundberg8194 Год назад
@@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.
@zerosandones7547
@zerosandones7547 10 месяцев назад
what's the ide/code editor here? (newbie question)
@Z3rgatul
@Z3rgatul 10 месяцев назад
Neovim
@tinymints3134
@tinymints3134 10 месяцев назад
(string[])[0] can be undefined
@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!
@stele9164
@stele9164 Год назад
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.
@Solsenderz
@Solsenderz 6 месяцев назад
Can you guys tell me, what colorsheme is it?🔥
@FeLiNe418
@FeLiNe418 Год назад
You know when you run ts code, all type information disappears?
@taragnor
@taragnor 7 месяцев назад
Well technically you don't run TS code, you run JS code.
@call_me_chip
@call_me_chip Год назад
you should be a QA dev for typescript.. this is a huge bug that made it into production
@xshady2967
@xshady2967 Год назад
typescript is unsound and it's known
@ex-xg5hh
@ex-xg5hh Год назад
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.
@thomassynths
@thomassynths Год назад
This is why we have languages like Haskell with real type systems.
@bert88sta
@bert88sta Год назад
Yeah most of the good stuff in rusts types is heavily inspired from ML and Haskell
@Imaltont
@Imaltont Год назад
Time to get the Elm train going.
@davkk
@davkk Год назад
elm, purescript, fable - this is the way!
@asdqwe4427
@asdqwe4427 Год назад
nah, it's better to wait for wasm 🦀
@lpanebr
@lpanebr Год назад
You're going to piss Teo. I like it!
@Ultrajuiced
@Ultrajuiced Год назад
Is this really with all strict compiler options on?
@ThePrimeTimeagen
@ThePrimeTimeagen Год назад
yes
@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”
@whosgrshh2596
@whosgrshh2596 Год назад
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 10 месяцев назад
Why would you want to do this?
@homelessrobot
@homelessrobot 9 месяцев назад
@@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.
@yoni532s9M5w
@yoni532s9M5w Год назад
Union types as array items are a total mess overall imo
@dmnkb
@dmnkb Год назад
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 Год назад
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 10 месяцев назад
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.
@wanstalker107
@wanstalker107 6 месяцев назад
3:00 Missing "as const" ?
@BlurryBit
@BlurryBit Год назад
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 Год назад
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);
@dealloc
@dealloc Год назад
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 Год назад
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 Год назад
@@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 Год назад
​@@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.
@technologyondemand4538
@technologyondemand4538 Год назад
This would never happen in rust :p
@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"); }
@cem_kaya
@cem_kaya Год назад
As a C++ (rust --) developer this hurts my brain. why
@connorallen162
@connorallen162 Год назад
weed onwee pweese 🥺 👉👈
@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 Год назад
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 Год назад
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
@turolretar
@turolretar Год назад
Js ❤
@WingXBlade
@WingXBlade Год назад
avoid mutations like the plague!!
@howuseehim
@howuseehim 9 месяцев назад
What happened?
@Zooiest
@Zooiest Год назад
I wonder if TS could work around that with primitive wrappers and Object.seal() and Object.freeze()
@tilakmadichettitheappdeveloper
Okay...but what's the solution ? Use JSDocs ????
@ThePrimeTimeagen
@ThePrimeTimeagen Год назад
no, there is no solution, you have to literally avoid these issues.
@idonoD
@idonoD Год назад
Soulsucking way of at least getting it caught during runtime is to freeze the object and it's properties
@9SMTM6
@9SMTM6 Год назад
@@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 Год назад
@@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);
@jalalle1995
@jalalle1995 Год назад
Why we're taking too many roundtrips to achieve safety, why not just using Rust
@gabrielstellini
@gabrielstellini Год назад
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 Год назад
It is not just an obfuscation for any. You cannot say for e.g. const x: CustomType = 5;
@theIbraDev
@theIbraDev 10 месяцев назад
Can't you use "As const" ?
@muzam1l
@muzam1l Год назад
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[]` 🤷
@vitiok78
@vitiok78 Год назад
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 8 месяцев назад
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.
@blenderpanzi
@blenderpanzi 10 месяцев назад
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.
@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 Год назад
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.
@REPOORTEHT
@REPOORTEHT 9 месяцев назад
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...
@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
@nomoredarts8918
@nomoredarts8918 Год назад
It's a language level feature, typescript cannot do this
@lalit5408
@lalit5408 Год назад
Would be funny if TS was a language. But all good.
@AhmedSLilah
@AhmedSLilah Год назад
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*"
@carriagereturned3974
@carriagereturned3974 Год назад
ts stands for trashscript
@wadecodez
@wadecodez Год назад
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_ Год назад
It exists: purescript
@wadecodez
@wadecodez Год назад
@@belst_ lol no. anything but ts deviates too far from the base syntax. makes it hard to adopt on existing projects.
@guillemgarcia3630
@guillemgarcia3630 Год назад
@@wadecodez I wish I could say rescript :/
@ethanholz3733
@ethanholz3733 Год назад
Me watching your TS videos makes me love that I don't write TS.
@oussama40612
@oussama40612 Год назад
Where are the solutions for these
@ChamplooMusashi
@ChamplooMusashi Год назад
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 Год назад
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.
@trustytrojan
@trustytrojan Год назад
at this point just make web servers in java or rust
@tokiomutex4148
@tokiomutex4148 Год назад
Structural typing sucks
@ThePrimeTimeagen
@ThePrimeTimeagen Год назад
it does
@FryuniGamer
@FryuniGamer Год назад
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 Год назад
It does not suck, it ducks.
@tokiomutex4148
@tokiomutex4148 Год назад
@@michaelbitzer7295 lmao
@marcusrehn6915
@marcusrehn6915 Год назад
You love your mutations don't you? 😉
@ElderSnake90
@ElderSnake90 Год назад
pawnstars-those-bastards-lied-to-me.jpg
@morra82
@morra82 4 месяца назад
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
@muhammadsalmanafzal9396
@muhammadsalmanafzal9396 Год назад
if you use strict type checking, the compiler tells you to explicitly check type which could remove this vagueness.
@dealloc
@dealloc Год назад
This is strict mode.
@DubiousNachos
@DubiousNachos Год назад
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.
@NeoChromer
@NeoChromer Год назад
In the first example I literally see nothing wrong?
@QckSGaming
@QckSGaming Год назад
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 Год назад
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
@nullignore99
@nullignore99 Год назад
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.
@aymanpatel5862
@aymanpatel5862 Год назад
Bro wtf. Typescript is just as dynamic as JS. Why tf do I need TS if i can blow types with JS itself.
@idontwanttostealausername9497
*Proceeds to remove all type definitions and replace everything with any in his TS repos*
@user-ko2yo3zx8h
@user-ko2yo3zx8h Год назад
it turns out you just cant fix JS
@josevargas686
@josevargas686 10 месяцев назад
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)
@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.
@msrini
@msrini Год назад
May I know the reason for choosing the numbers as 69 and 420?
@DubiousNachos
@DubiousNachos Год назад
They're funny.
@jeffreyblack666
@jeffreyblack666 4 месяца назад
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.
@dasten123
@dasten123 Год назад
And this Rust crap does it better?
@ThePrimeTimeagen
@ThePrimeTimeagen Год назад
Yes, 100x
@Z3rgatul
@Z3rgatul 10 месяцев назад
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
@pinatacolada7986
@pinatacolada7986 11 месяцев назад
HTML, CSS and JS used to suck, back in the day, so you needed junk like jQuery, Sass, Less, Typescript, Bootstrap etc. But now, in 2023, HTML, CSS and JS have advanced so far, there's no need for this extra nonsense. You can actually just code the basics in the native language. The only thing that still sucks is SQL - everyone needs to be using Neo4j databases, it's a game changer.
@ShaggyKris
@ShaggyKris Год назад
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.
@mag2XYZ
@mag2XYZ Год назад
But it should be a void function, it's not returning anything
@9SMTM6
@9SMTM6 Год назад
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.
@idiota-w-swiecie
@idiota-w-swiecie Год назад
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
Далее
Next Generation TypeScript | Prime Reacts
24:45
Просмотров 95 тыс.
err != nil Is GOOD? (And Why)
7:19
Просмотров 86 тыс.
Can this capsule save my life? 😱
00:50
Просмотров 3,5 млн
Type your functions in TypeScript and SAVE TIME
8:31
Why I Like Programming in C.
3:16
Просмотров 25 тыс.
3 Questions: You Don't Know JavaScript | Prime Reacts
30:44
How Slow Is JavaScript? | Prime Reacts
15:34
Просмотров 172 тыс.
I Cannot Believe TypeScript Recommends You Do This!
7:45
Thoughts About Unit Testing | Prime Reacts
11:21
Просмотров 207 тыс.