The code is cleaner when splitting it out into components, agreed; however, I don’t think it should be the job of a component to decide whether itself should be rendered or not (e.g. the Sidebar component returning null if the sidebar is not supposed to be open). I think the caller/consumer of a component should decide whether it’s to be rendered or not, and components themselves should be just that: The component markup and any features/state _within_ the component.
100% agree if you're talking about actual independent components, however this makes more sense if you're just breaking out small parts within a single file, and those "sub components" aren't externally accessible. This is pretty much refactoring out a function and failing early to make it more concise.
The problem with breaking it out into components is that it's verbose. Most people using && use it for small pieces of content, not anything huge that makes sense to break out into its own component.
well yes we mostly use && for small little things, but as he said when you start to fill up your markup with too much ternary (especially nested ones at that) then it's a good idea to break it out into a component to keep it clean. He didn't say don't use them at all, just don't overdo it
And now you have to go find those components, to figure out what happens if the state changes. For small stuff, I rather use `&&` and `? :` every time.
8:40 T[0] is still different from T[number]. I know you know, but some viewers could be misled. for example for a type like this: [number, string]. Typescript knows your first element is a number, your second is a string, and it has two elements. So T[0] will be number while T[number] will be number | string.
Was about to point out the same, but while double checking in ts playground i noticed the example wasn't recursive either. To unwrap arbitrarily nested arrays, it would have to be Flatten in the true branch
Can someone please explain this a little further? I've re-watched this part multiple times and I still don't quite understand why you need to do this (also a typescript n00b)
@@happylittlesynth Imagine types as being a special kind of value. We don't know anything about T at first. When you do T extends any[], you are checking if T is included in the definition of any[], meaning, T must be an array. We still don't know what T contains. It could contain a type, several types, etc. T[0] tells typescript to take the first element of T. If T is an array of numbers number[], it'll be number. If T is an array of strings string[], it'll be string. If T is an array contains both strings and numbers (string | number)[], it'd be string | number. But if string has a precise type, for example [number, string], we know three things: 1) it has exactly two values 2) the first value is always number 3) the second value is always string. As such, T[0] would be number. 0 can be thought of as the type of any number whose value is 0. Because the definition is so precise, it's just 0. When saying T[number], we are asking typescript what the result would be of indexing over the type of any valid number. It's a wider definition, a different type, but the same operation. The valid numbers here are 0 or 1, so the result is either number or string, number | string. Thus, in our example: T[0] is number T[number] is number | string If you wanted to use the type of the values inside T later on, using T[0] could create a bug if T contains more than a single type. When you're making a generic like this, you don't know how it could be used in the future, so you want to cover your bases. What if you forgot days later that your generic flattening type just doesn't work with more than one type per array? So while generics should be as tight as possible for clarity, you should also avoid losing information along the way.
I don't necessarily dislike the choice prettier made. But having the parenthesis at the end of the line while the colon is at the start is a net negative to me. I'd much prefer for both to be at the start. With your example, I'd prefer: type Something = T extends string ? T extends "a" ? "a" : "b" : T extends number ? "number" : "other" If you if else can't be at the same level, I'd at least like both paths to be, just like in regular programming. Questions in languages are already annoying in that you must rely on other clues to determine it is a question, as the question mark comes at the end. Taking hints from natural language isn't always great.
No one likes this. I don’t like it either, but there is still less adaption of other frameworks like svelte or solid in the industry. Vue is the only one I have seen quite a bit of decent adaptation. Blaming framework users is a cop out thing to do when you fail to see what framework the market still uses and continues to keep using. He is showing how we can use best practices to tackle this
I tend to overuse ternaries because I wasnt sure if creating a bunch of components with properties that drills was good practice. Thank you for sharing your opinion! I will try to change my practice and see if I find a preferred one
Well, I often use ternaries (in rare cases even nested ones) to make the code in my webpage's JS smaller and less to type. For example, I have a function that generates a lot of numbers that in the end should be packed into a string with the backtick string functionality. Instead of writing an if/else statement I just type the string template and in the optional parts I put a ternary to either place the number there or if it's a certain value put a placeholder or nothing there.
I do quite like ternary operations in my code, but I do find that a good rule of thumb is to fit them in a single line. If one spans multiple lines, consider another option. As small as possible, and no nesting. Then they become clarifications rather than obfuscations.
In the first example you can just map over the array without any checks: mapping over an empty array renders nothing. No checks on contacts.length are necessary.
Sometimes you would want to show something to the user that the array is empty, like a message ("You don't have any..."/"This list is empty") or an icon or something, that's where ternaries (or just a simple if-else) is good for.
@@mintlatacouldn't you just pass the html as a string to a variable and then after mapping check if that variable is null. If it is return your special message if not return the markup. That is verbose but much cleaner IMO
@@mintlata yeah, and often you don't want to render the surrounding wrapper at all for 0 elements, then lifting the length check higher up makes more sense than in the example.
Amen brother. I've been a front-end developer for ~20 years and a staunch advocate for avoiding nested ternaries. I haven't really found any cases where I've preferred them. Ternaries in general can be hard to follow, consistent formatting across the JS/TS/JSX landscape at least makes them more palatable, but agree that nested is still awful. I would much rather see an "ugly" if/else chain that allows for easier to (mentally) parse code.
@@chrishyde952 yes, ternaries have been around for much longer than that, in JavaScript they were part of the original language spec. While I agree that whether or not I use nested ternaries is a preference, I disagree with the premise that just because something is valid code it shouldn't be criticized if the way it is used makes it harder to read the code. I'm not _dismissing_ ternaries, but rather advocate for taking the most readable and understandable approach. Which IMO in many cases means that nested ternaries would be better off being rewritten.
So Theo you're ok with returning null from a component? I've always avoided it, and prefer to not even call the component in the case it shouldn't render. Do you have any reasons to use null?
my favorite ternary syntax looks like this: condition ? if-value : else-value then all nested ternaries get indented as well: condition1 ? condition2 ? ifvalue2 : elsevalue2 : elsevalue1
6:10: you may use ternary inside your component to avoid multiple return paths. function Sidebar(props: ...) { return (props.sidebarOpen === false) ? null : { Sidebar : }; } Turn that into an arrow for even less bloat: Sidebar = (props: ...) => (props.sidebarOpen === false) ? null : { Sidebar : }; } Same for UserInfo. Ternary are not the problem. Mixing code and html definitely is.
I haven't actually made use of ternaries in js but my immediate thought was regarding your usecase: What if you use the ternary like it's an if guard? Invert the boolean check so null is specified first.
I like have a rule on using ternary 1.) Don't make it complex. 2.) Avoid Nesting if possible. 3.) Use component if really big (optional). 4.) (Tip) If possible I make my ternaries readable. Idk if someone does the same (Let use the example in this video) { (sidebarOpen === false) ? //just by reading this line you know'll encounter a ternary. null : //if userInfo is false Don't render just show the sidebar Else render it with the sidebar. }
Happy new year. Creating a bunch of functions and components can also just make a file hard to organize.Personally I had to develop a system where I move functions around so similar functions are closer in the file
Too many files. Too many components In a file. I have heard this countless times . All of this is just messy. We are convincing ourselves that this is better than that. I think the whole jsx was a mistake. But there is no better alternative aswell.
The JSX case is a great example of overengineering like with creating factories for everything in joke “senior” code. React docs itself recommend to check for condition before rendering component like {showUserInfo && }. The early returns in function is more like an anti-pattern until your are doing something like Chain of Responsibility (which is not a case here).
I had a discussion about whether we should allow ternary. In the end I managed to convince to prevent using a ternary operator in a ternary operator. It's also that the operator precedence of the ternary operator over other operators differs between languages. And it's also different on right or left associative (so calling the right ternary or the left operator first). PHP even changes it after version 8! Still when it comes to clarity && is not clear to everyone. I started programming in C++, so I'm used to these types of hacks.
I've always ended up putting a lint rule to block and nested ternary. If you need that level of complexity to figure out what you should be rendering just make a function
One of the things I liked about templating languages like handlebars was how readable they were (of course they have their own shortcomings). I do agree with the arguments for encapsulating the ternary behavior for readability. However I also agree with the argument that doing so will put the responsibility on the encapsulating component for worrying about state outside of itself, and the parent should probably be the one determining whether a component should show or not. What about a nice middle ground such as creating a conditional component to handle this behavior? If we have a component that encapsulates the behavior, we can leave the inner component to worry about its own state, and the parent still gets to determine whether the child renders or not: //
I think kotlin does this better because if-else can be used as expressions. For example: val number = 0 val result = if number > 1 "greater than one" else "not greater than one" IMO that's just a better solution.
Rule of thumb I use in the code review is that the "short branch" should go first, then you can reduce the mental cost of keeping the context of the ternary
🎉 Yup. This is really good practice. I use tenraries a lot but never in react jsx but even still, I'm finding more and more just preferring to use an if guard statement.
@9:23 ternaries are an expression (meaning they evaluate to values), while ifs are just a control flow statement that don't resolve to a value on their own
Vue and Svelte's idea of "single file component" is cool, but it limits you to one component per file ... it makes it costly to split a component in several smaller ones. React may have a lot of problems, but one of its best feature is that it is super easy to make a bunch of small components.
@@JonathanRose24 I wouldn't necessarily call SFC a mistake, but certainly a trade off. If it's the price to pay for, say ... scoped CSS ... I would certainly hesitate for a bit. (I might be wrong, but I have a feeling that CSS modules, Tailwind and StyleX were created in response to this weakness in React) On the Svelte side, I look forward to Svelte 5's new "snippets" feature: it seems to fit the "small private component" niche nicely. (it's just a shame that they had to resort to this *kind-of-component-but-not-really*, whereas in react-land, you'd just use regular components)
6:35 This pattern could lead to performance implications though. What if Sidebar needed to query some user data using a hook? Since hooks can't be called conditionally, we'd have to run the query even if the sidebar is closed. It would be better to just not render Sidebar at all by conditionally rendering the component in the parent
I am of the camp that pattern matching should be in every language as it is the most readable option for returning values based on conditions - especially when multiple values are possible.
Why didn't anyone think of making a Show component like in Solid for React and an If component? But now I'm writing my UI library in which I added this, first of all, and other components that help to control my Switch type markup, and the most interesting thing is the Slot, which for some reason no one made for React
I think short and concise with proper formatting, where you can see the entire functionality in one reasonable component is much preferable to breaking up everything into an endless number of little components which all they do is an if check and then depend on a number of other components. I find trying to track down many components and how they fit together in my head much harder than reading a component that's a little bigger because it has a couple conditionals in it.
with angular you can just use ng container and ngIf ngFor without trading in clarity. With the new control flow syntax they may introduced the same issue.
I agree with the top to bottom going into left to right, that is annoying to parse... The linter we use forces all JSX to be wrapped in parens with new line after start/before end. This way it's just reading top to bottom still even with the ternaries. Granted, I also would push back on nested ternaries. I'd rather just put the new ternary in a new component. Though, I am pretty interested in trying to do the return null flow you show here instead of ternaries in general.
I really like that new formatting. Reads almost like if-elseif-else code. Although I agree that you should avoid nested Ternaries as much as possible. But the next time I run into code where a prior dev has a bunch of nested Ternaries on a single line, I'll happily reformat it to this before trying to read it.
I'm glad I found your channel this year. I've been at this game professionally for 16 years now, 28 total, but I usually come away with some great insights from your content. So thank you and I hope you have a wonderful 2024!
I tend to find it more readable if the syntax is fairly terse (not ridiculously terse like array programming languages) and unless you're reusing a function or component; I usually prefer large functions/components over a bunch of small ones. While I've always been prone to write rather large functions; when I was younger I found myself splitting out in to sub-functions a lot more than I do now, these days I will usually only split out sub functions if it makes the code significantly shorter or of it makes scoping easier... I will often even often restructure the code so that I can use fewer functions if possible. End I wonder if it's not only experience/old-school preferences, but also physical aging. I need bigger fonts to see well these days (I no longer prefer dark mode during daytime either because my eyes need more light); and I've always preferred usning a single and fairly small monitor for coding (too big screen means a lot of wasted white space blasting light at me, or distractions in my peripheral vision if I use non-maximised windows); so with a larger font I have less screen space than before and code is much easier to reason about when you can see all the relevant parts at once. And while ternaries have the disadvantage of using very small symbols; at 18 pt font size they're still quite visible. And I really can't stand (and I never have) having the logic of some feature split over several files. I despise languages that require you to put each thing in it's own file, because those implicitly requires you to split your logic over several files if there is any code re-use at all. But I'm sure for the kids who write code in microscopic fonts such as 10 or even 8 pt it could be hard to see a colon since it's a just two faint antialiased blobs. And if you write in microscopic fonts, and maybe even use huge and multiple monitors, maybe having the logic split up into dozens of verbose named functions or components doesn't bother you as much as it bothers me, because you can see the whole content of maybe four 80 line files at once; while I normally can only see around 35 lines of one single file at the time.
What we really need is expressions like rust has, like const x = { if(something) return 1; else if(somethingElse) return 2; return 3; } Same as immediately called function, just less awful. const x =:{something} is way better than const x = (()=>{something})(). Even if it does have a very upset face in the front, it's probably way less upsetting than deep ternaries. Maybe even omit curly bracers, like const x =: if(a) return 1; else if(b) return 2; else return 3 or even omit returns. Though I can already see people doing side effects in this, that would be a mess. Not that you can't do side effects with ternaries, I'm guilty of console logging stuff during jsx render for debugging myself. I've used them, it's been a pain. Especially whenever there's something recursive to do with a deep type that has to support plain types, objects and arrays. I don't think there's a way around doing T extends ValueType ? ... // string, bool, number etc : T extends any[] ? ... // arrays, you can recurse with the single element with T[number] : ... // objects, do a mapped type or something like that.
Any time I make a new component my job requires me to make a new unit test suite and Storybook story for it so I tend not to make new components if I don't absolutely have to
In JSX I really like the component by SolidJS, you can very easily implement it in react: function Show({ when, children }) { if (!when) return null return children }
I love javascript, truly. I don't like jsx. I just program everything in javascript, html, and css. I like it more that way, and I get to learn how to make things fast by understanding javscript better, instead of muddling through with react and compilation which slows me down too much, honestly. The only thing that really slows me down now, is finding why the NaN value is happening after refactoring, but I'm working on a tool to help with that... Maybe it will be out soon. In the mean time, it's happened so often now that I know what to look for. 9 times out of 10, it happens because of division or other mathematical operations with an undefined variable.
Maybe this is just a me thing, but whenever i shuffle through the "when to use what" and ternaries comes to mind, i make sure that either of my arms is "meaningful". To me, falsy values on either arm are not "meaningful". At least within the context of React, i only use ternaries to render either component A or component B. NEVER whether a component should be rendered or not. There are usually better options at that point.
"&&" operator isn't great in JSX if you aren't explicit about your code. Just use proper length checks such as ".length > 0" or ".length === 0" and just avoid using JS quirks that many devs don't even know how they work. It will also be way easier to read your code because it's super clear what condition you are checking here and not depending on "falsy" quirks of JS. I mean, use explicit checks even outside of JSX, don't try to be super smart about your code, but rather make it clear what are the intentions of that code. Regarding ternaries, I dislike using ternaries in JSX to render "null" as the "else clause" because it's pointless to me. If I use a ternary in JSX, I want to render something meaningful for both branches and rendering "null" isn't meaningful to me and just introduces unnecessary nesting and dangling nulls in code. I'm ok with using ternaries to render simple markup, but if I get to nested ternaries, I abstract that into some if/elseif/else component as shown in your example.
Scrolling the comment section all the way down. See only one or two comments like yours mentioned about the condition itself. People are abusing ".length" as a boolean. Why can't we just check/compare the length via ".length > 0"" as the original intention to make it as a proper boolean condition instead of trying to do something like Boolean(length), !!length or what so ever. Just to save few key strokes? This is not something really arguable, lately I really start to doubt the average js dev ability.
@@doc8527 Yeah I never understood why people use ".length" without actually comparing it to something. It just makes code confusing. To me it's just harder to understand at a glance what is the intention of that code and it can also lead to weird cases because people don't actually understand how it works.
I never found nested ternaries good or even elegant. I one of ternary to set a value, based on a condition ok. But nesting them just feels hacky. I always loved early returns since they day I first did that pattern. It just makes so much more sense do go "You are not that? Ok go away!". Just feels like a bouncer at a door that just doesn't let people in when they wouldn't fit. AND you can even optimize performance with having the "easy" checks first. I know that you can do that with ternaries, but it's just so much easier to see when everything is pretty much sequential. With ternaries it has more a this feeling that you have 8 bouncers that are sending the people into different queues that have other bouncers. If your if-else -rewrite starts to get ugly, the best way is not to just fall back to the old solution, but to think about better structuring what your if-else does, and if you really need to nest that deep. And if you really need to. Better ugly and readable, then fancy and you needing 3 red bulls and a phd to grasp it.
@@GenghisD0ngIMO it's better and much faster to put a breakpoint. You basically get your own steering wheel, pause and inspect each line, giving you better understanding of how a code works without reading the whole program.
I'm a bit concerned that Prettier is trying to justify that nesting ternaries is a good idea. the only case i ever see for a ternary is if you're doing a conditional spread in an object or array and then doing `{ ...myObject, ...(isThisThing ? { field: 'some value' } : { otherField: 'some other value' ) }` because although you can do ` ...( isThisThing && { field: 'some value' } )` that same doesn't work with arrays and you'll have to ternary arrays. So it just keeps that syntax similar. i do agree that if/else is messy, but that's why doing early returns with if checks is a lot cleaner than if/else blocks. A ternary is used it if it's a dichotomy, so just doing if (!notThing) { return } and then having your other logic continue below for me is a lot easier to read, and can often stop if/else nesting as well. And if it has a lot of conditions that's where switch statements or maps are useful. Because i totally agree having good legibility in your code is key, being able to come back into something months down the line and not feel like you're tiptoeing through cryptic but concise code is a godsend for refactoring, making changes, or just reviewing
For some reason, my team is absolutely obsessed with tenaries. Everything that is just an if else with returns is going to be refactored into tenaries regardless of how applicable it is or not
I think && is just fine if you don't overdo it. I mean if the codepiece is really complex you may outsource it into its own component, but even then i would put the condition into the outer component with &&, since it is really easy to read and understand for me that way.
I believe that programmers, when given the choice, will always choose what is most concise and "easy". In this case, it's the ternary operator versus an if else - but I disagree with the point that they are simpler, as the guy in the future will have much more trouble dealing with a complex ternary operator than a complex if else. And I wholeheartedly believe that having ternaries in a programming language is a bad design decision. Even though there are cases where a ternary operator is better than an if else operator, there are far more cases where a simple and descriptive if else is better. In the 10% of cases that we lose, we win in the 90% that remain. I'm a big proponent of strict and firm ways of doing things. That may be one reason why I like Go and Rust, even if on different ground.
Not about ternaries, but I think you should never do this kind of check if a value is falsy like "something.length && ..." or "if(a.length)". It is a tiny bit more work to write out the full condition with "!== 0" but it makes things more clear and more well defined. If youre not careful you will also end up doing weird things you didnt intend to do. For example, if you want to check that a variable str that could be null or a string is not null and you do this implicit check, an empty string would be handled the same way null would be handled. And even if you want to do that, write out two conditions because then at least people will know that both parts of the condition are intended and you didn't implicitly check for multiple conditions by accident. And these hacks people do with the !! are the weirdest thing. You feel so cool when you shorten your code by 4 characters but all you're really doing is making your code less readable. An example of implementing ternaries well is Kotlin in my opinion. The language doesn't have the classic ? and :, but instead an if statement is an expression. You can do something like "val x = if(...) { ... } else { ... }". It is more concise, allows you to handle cases where you have more than two possibilities in a readable way (else if rather than chained ternaries) and you can also write proper code into the blocks and arent just limited to returning a value.
Ngl I started the video assuming I would disagree considering how popular ternaries are in JS, but I think I agreed on basically everything. I do wish they had the inline syntax that python has with inline If's instead of ternaries since I find that much more readable
I think the only real problem here is bad usage of the tools available. Ternaries can be perfectly readable in simple assignments or in that specific line where you wrote ```userInfo ? Something : null``` But thinking that you should use a tool "everywhere" like the article suggests, it's just not viable. All the tools in the programming languages are there to be used whenever you're in need of them, if he used a Ternary ot solve a specific problem, then good for him, but not because of that, you're immediatly going to start using ternaries everywhere and avoid using logical ands. There's place for everything, and everything goes in its place, so I don't agree at all that ternaries are bad, or that you shouldn't use logical ands, it's just a matter of propperly balancing your use of the many language tools the programming language gives you to write readable and efficient code.
Many languages don't allow early returns from functions and have only the ternary if. Look up "cond" in lisp for an example of this. The syntax you explore later on in the video is really just an approximation of cond. I think once you've seen a language that does ternaries well, you understand that they are better in most cases.
Nested ternaries were extremely common in Verilog before tool support for “if” statements were strong. Look in any legacy Verilog file and you will see the same thing
Sometimes I wonder why people would even need to do a `contacts.length && contacts.map`. The map would simply just not return anything if the list is empty, so it's kinda pointless to check for it.
4:45 but in vue and probably in svelte (i haven't tried svelte yet) you cannot just return null like in react. In those frameworks you always need to define condition in the template.
Seeing the nightmare that is TS type definitions, I personally prefer just writing complex typed code in something else (C++, Rust, etc.) and using WASM FFI to access results. TypeScript is doing the best it can with a bad situation in trying to add typing to language designed not to have it. I'd rather keep the JS/TS small, simple, parsable, etc., and offload the difficult static analysis to a strongly typed language.
The code is cleaner when splitting it out into components, agreed; however, I don’t think it should be the job of a component to decide whether itself should be rendered or not (e.g. the Sidebar component returning null if the sidebar is not supposed to be open).
I think the caller/consumer of a component should decide whether it’s to be rendered or not, and components themselves should be just that: The component markup and any features/state _within_ the component.
100%. By adding in the conditional, you're now making the component consider state outside of it's immediate concern.
Idk why but kinda agree with this.
Can we have this discussed cos this feels important
100% agree if you're talking about actual independent components, however this makes more sense if you're just breaking out small parts within a single file, and those "sub components" aren't externally accessible. This is pretty much refactoring out a function and failing early to make it more concise.
While I agree this is just contrived example.
I use ternaries a lot in Svelte property bindings. When it's a simple case, it's way easier than creating a whole function for it.
The problem with breaking it out into components is that it's verbose. Most people using && use it for small pieces of content, not anything huge that makes sense to break out into its own component.
well yes we mostly use && for small little things, but as he said when you start to fill up your markup with too much ternary (especially nested ones at that) then it's a good idea to break it out into a component to keep it clean. He didn't say don't use them at all, just don't overdo it
Another solution with the control flow discrepancy, you can treat it more like a ternary guard clause:
{!arr.length ? null
: arr.map(el => {return (el % 2 === 0) ? null
: {el}})}
Makes it a little cleaner when your null case isn't so far away from the ?
And now you have to go find those components, to figure out what happens if the state changes. For small stuff, I rather use `&&` and `? :` every time.
I think verbose and easily readable is better than terse and hard to follow
I have slowly come to link "verbose" as a synonym for "maintainable".
It’s crazy that it’s about to be 2024 and devs aren’t just returning null for React components.
It was 2024 for me when you commented this
Explain? Is this a jab at react?
@@nathangwyn6098 it’s a jab at React devs.
@@nicosoftnt what time zone?
@semyaza555 I don't get it though lol ita going over my head. If you just return null with all your components nothing will be rendered? X.x
8:40 T[0] is still different from T[number]. I know you know, but some viewers could be misled.
for example for a type like this: [number, string]. Typescript knows your first element is a number, your second is a string, and it has two elements. So T[0] will be number while T[number] will be number | string.
Was about to point out the same, but while double checking in ts playground i noticed the example wasn't recursive either.
To unwrap arbitrarily nested arrays, it would have to be Flatten in the true branch
well this is done with T extends (infer R)[] ? R : T, not T[0]
@@andreasherd913 T[number] as in the example works just fine, I see no need to infer a second temporary type, it's just extra processing for your IDE.
Can someone please explain this a little further? I've re-watched this part multiple times and I still don't quite understand why you need to do this (also a typescript n00b)
@@happylittlesynth Imagine types as being a special kind of value.
We don't know anything about T at first.
When you do T extends any[], you are checking if T is included in the definition of any[], meaning, T must be an array.
We still don't know what T contains. It could contain a type, several types, etc.
T[0] tells typescript to take the first element of T. If T is an array of numbers number[], it'll be number. If T is an array of strings string[], it'll be string.
If T is an array contains both strings and numbers (string | number)[], it'd be string | number.
But if string has a precise type, for example [number, string], we know three things:
1) it has exactly two values
2) the first value is always number
3) the second value is always string.
As such, T[0] would be number.
0 can be thought of as the type of any number whose value is 0. Because the definition is so precise, it's just 0.
When saying T[number], we are asking typescript what the result would be of indexing over the type of any valid number.
It's a wider definition, a different type, but the same operation.
The valid numbers here are 0 or 1, so the result is either number or string, number | string.
Thus, in our example:
T[0] is number
T[number] is number | string
If you wanted to use the type of the values inside T later on, using T[0] could create a bug if T contains more than a single type.
When you're making a generic like this, you don't know how it could be used in the future, so you want to cover your bases. What if you forgot days later that your generic flattening type just doesn't work with more than one type per array? So while generics should be as tight as possible for clarity, you should also avoid losing information along the way.
I don't necessarily dislike the choice prettier made. But having the parenthesis at the end of the line while the colon is at the start is a net negative to me.
I'd much prefer for both to be at the start.
With your example, I'd prefer:
type Something =
T extends string
? T extends "a"
? "a"
: "b"
: T extends number ? "number"
: "other"
If you if else can't be at the same level, I'd at least like both paths to be, just like in regular programming. Questions in languages are already annoying in that you must rely on other clues to determine it is a question, as the question mark comes at the end. Taking hints from natural language isn't always great.
The problem is JSX not ternaries. React users pretend JSX is superior in every way, when it can't handle basic loops or conditionals without a hack.
But the alternatives are as ugly as jsx
Blade templates are pretty nice
No one likes this. I don’t like it either, but there is still less adaption of other frameworks like svelte or solid in the industry. Vue is the only one I have seen quite a bit of decent adaptation. Blaming framework users is a cop out thing to do when you fail to see what framework the market still uses and continues to keep using. He is showing how we can use best practices to tackle this
Loops? Since when has array methods not been enough? It's easy to read as well.
I love how compose lets you do simple if-else without hacks like this
I tend to overuse ternaries because I wasnt sure if creating a bunch of components with properties that drills was good practice. Thank you for sharing your opinion! I will try to change my practice and see if I find a preferred one
I hope you discuss more technical topics like this.
I already was subscribed, so I don't understand why the type error, but I loved the gag! :)
Well, I often use ternaries (in rare cases even nested ones) to make the code in my webpage's JS smaller and less to type. For example, I have a function that generates a lot of numbers that in the end should be packed into a string with the backtick string functionality. Instead of writing an if/else statement I just type the string template and in the optional parts I put a ternary to either place the number there or if it's a certain value put a placeholder or nothing there.
I do quite like ternary operations in my code, but I do find that a good rule of thumb is to fit them in a single line. If one spans multiple lines, consider another option. As small as possible, and no nesting. Then they become clarifications rather than obfuscations.
6:57 loved that type error
In the first example you can just map over the array without any checks: mapping over an empty array renders nothing. No checks on contacts.length are necessary.
Sometimes you would want to show something to the user that the array is empty, like a message ("You don't have any..."/"This list is empty") or an icon or something, that's where ternaries (or just a simple if-else) is good for.
@@mintlatacouldn't you just pass the html as a string to a variable and then after mapping check if that variable is null. If it is return your special message if not return the markup. That is verbose but much cleaner IMO
@@mintlata yeah, and often you don't want to render the surrounding wrapper at all for 0 elements, then lifting the length check higher up makes more sense than in the example.
Just put !!contacts.length
@@ea_naseer I’m not sure I understood your idea. Can you give an example?
Top tier subscription reminder with the literal type 😂
Made me rage quit the video 🤷🏻♂
In my opinion, the example with more components was not "clearer", but for a big ternary example, I can see why it would be better.
You don't have to use ?:, you can instead use !!
Separation of concerns should be the first goal for sure.
Amen brother. I've been a front-end developer for ~20 years and a staunch advocate for avoiding nested ternaries. I haven't really found any cases where I've preferred them. Ternaries in general can be hard to follow, consistent formatting across the JS/TS/JSX landscape at least makes them more palatable, but agree that nested is still awful. I would much rather see an "ugly" if/else chain that allows for easier to (mentally) parse code.
Been doing this for a long time too and I flat out can’t read nested ternaries.
Did ternaries even exist 20 years ago? Not being able to read valid code isnt an excuse to dismiss it. Its a preference
@@chrishyde952 lua, c++, c..... nothing new
@@chrishyde952 yes, ternaries have been around for much longer than that, in JavaScript they were part of the original language spec.
While I agree that whether or not I use nested ternaries is a preference, I disagree with the premise that just because something is valid code it shouldn't be criticized if the way it is used makes it harder to read the code.
I'm not _dismissing_ ternaries, but rather advocate for taking the most readable and understandable approach. Which IMO in many cases means that nested ternaries would be better off being rewritten.
So Theo you're ok with returning null from a component? I've always avoided it, and prefer to not even call the component in the case it shouldn't render. Do you have any reasons to use null?
same
Keep doing that, passing a boolean to show or not the component is just dumb, imho 😊
My code review notion, see more than 1 level of ternaries, your PR is rejected.
my favorite ternary syntax looks like this:
condition
? if-value
: else-value
then all nested ternaries get indented as well:
condition1
? condition2
? ifvalue2
: elsevalue2
: elsevalue1
6:10: you may use ternary inside your component to avoid multiple return paths.
function Sidebar(props: ...) { return (props.sidebarOpen === false) ? null : { Sidebar : }; }
Turn that into an arrow for even less bloat:
Sidebar = (props: ...) => (props.sidebarOpen === false) ? null : { Sidebar : }; }
Same for UserInfo. Ternary are not the problem. Mixing code and html definitely is.
I haven't actually made use of ternaries in js but my immediate thought was regarding your usecase:
What if you use the ternary like it's an if guard?
Invert the boolean check so null is specified first.
First video I'm watching in 2024, have a nice year guys
I like have a rule on using ternary
1.) Don't make it complex.
2.) Avoid Nesting if possible.
3.) Use component if really big (optional).
4.) (Tip) If possible I make my ternaries readable.
Idk if someone does the same
(Let use the example in this video)
{
(sidebarOpen === false) ? //just by reading this line you know'll encounter a ternary.
null :
//if userInfo is false Don't render just show the sidebar Else render it with the sidebar.
}
Happy new year.
Creating a bunch of functions and components can also just make a file hard to organize.Personally I had to develop a system where I move functions around so similar functions are closer in the file
Too many files. Too many components In a file. I have heard this countless times . All of this is just messy. We are convincing ourselves that this is better than that. I think the whole jsx was a mistake. But there is no better alternative aswell.
Just Svelte, guys
Every day I see the wild shit like this, going on in web-dev languages, I become far more grateful for being a C# developer.
There’s a lot of weirdness going on in blazor too (I like it a lot, but there’s still weirdness)
Interrupted New Year’s party for the anxiety of ternaries.
The JSX case is a great example of overengineering like with creating factories for everything in joke “senior” code. React docs itself recommend to check for condition before rendering component like {showUserInfo && }. The early returns in function is more like an anti-pattern until your are doing something like Chain of Responsibility (which is not a case here).
“That’s not ugly, that’s readable b****” - 11:20 - Was not expecting that 😂. Great video overall
I had a discussion about whether we should allow ternary. In the end I managed to convince to prevent using a ternary operator in a ternary operator.
It's also that the operator precedence of the ternary operator over other operators differs between languages. And it's also different on right or left associative (so calling the right ternary or the left operator first). PHP even changes it after version 8!
Still when it comes to clarity && is not clear to everyone. I started programming in C++, so I'm used to these types of hacks.
OOP brain goes "this conditional statement should be several objects, actually"
I've always ended up putting a lint rule to block and nested ternary. If you need that level of complexity to figure out what you should be rendering just make a function
One of the things I liked about templating languages like handlebars was how readable they were (of course they have their own shortcomings).
I do agree with the arguments for encapsulating the ternary behavior for readability. However I also agree with the argument that doing so will put the responsibility on the encapsulating component for worrying about state outside of itself, and the parent should probably be the one determining whether a component should show or not.
What about a nice middle ground such as creating a conditional component to handle this behavior?
If we have a component that encapsulates the behavior, we can leave the inner component to worry about its own state, and the parent still gets to determine whether the child renders or not:
//
whoah... Theo did some OOP abstraction. What a time we live in.
I think kotlin does this better because if-else can be used as expressions. For example:
val number = 0
val result = if number > 1 "greater than one" else "not greater than one"
IMO that's just a better solution.
Hello wonderful person watching this wonderful video
Hello wonderful person opening the replies to the comment of a wonderful person saying hello to all you wonderful people
Rule of thumb I use in the code review is that the "short branch" should go first, then you can reduce the mental cost of keeping the context of the ternary
🎉 Yup. This is really good practice. I use tenraries a lot but never in react jsx but even still, I'm finding more and more just preferring to use an if guard statement.
The company I work for loves ternaries, complex ternaries, 5-10 nested ones.
@9:23 ternaries are an expression (meaning they evaluate to values), while ifs are just a control flow statement that don't resolve to a value on their own
Yes! A million times yes!! This debate has always been wrong, MAKE MORE COMPONENTS!
Vue and Svelte's idea of "single file component" is cool, but it limits you to one component per file ... it makes it costly to split a component in several smaller ones.
React may have a lot of problems, but one of its best feature is that it is super easy to make a bunch of small components.
@@tipeon you make a great point about Vue and Svelte. I think single file components is a mistake
@@JonathanRose24 I wouldn't necessarily call SFC a mistake, but certainly a trade off.
If it's the price to pay for, say ... scoped CSS ... I would certainly hesitate for a bit.
(I might be wrong, but I have a feeling that CSS modules, Tailwind and StyleX were created in response to this weakness in React)
On the Svelte side, I look forward to Svelte 5's new "snippets" feature: it seems to fit the "small private component" niche nicely. (it's just a shame that they had to resort to this *kind-of-component-but-not-really*, whereas in react-land, you'd just use regular components)
6:35 This pattern could lead to performance implications though. What if Sidebar needed to query some user data using a hook? Since hooks can't be called conditionally, we'd have to run the query even if the sidebar is closed. It would be better to just not render Sidebar at all by conditionally rendering the component in the parent
depends on the query you can choose to run the query only if sidebar is open
@@iceinvein Sure but there could be many other side effects as well. It could get messy to conditionally run all of them
Mind you that this is just an example, so different solutions are still required to be researched
Just create a component
I loved this video! Great content Theo!
I've always hated ternaries in markup, never thought of doing it this way, much better!
I am of the camp that pattern matching should be in every language as it is the most readable option for returning values based on conditions - especially when multiple values are possible.
Those ternary expressions were easily readable
2:50 Just invert the ternary and you get your top-to-bottom reading back. That's just what you ended up doing, but inside some components.
Happy new year Theo!!!
I definitely prefer top-to-bottom readability to DRY/concise.
Why didn't anyone think of making a Show component like in Solid for React and an If component? But now I'm writing my UI library in which I added this, first of all, and other components that help to control my Switch type markup, and the most interesting thing is the Slot, which for some reason no one made for React
I think short and concise with proper formatting, where you can see the entire functionality in one reasonable component is much preferable to breaking up everything into an endless number of little components which all they do is an if check and then depend on a number of other components. I find trying to track down many components and how they fit together in my head much harder than reading a component that's a little bigger because it has a couple conditionals in it.
Like isn't actually doing anything, itself! and you're repeating the names of these variables too. You could repeat this endlessly..
Ternaries are only to be used with one line assignments/function calls. This should be the standard.
JavaScript absolutely needs switch expressions like c# or the when expression like kotlin.
Kotlin also has if-else expressions instead of ternaries
This is pattern matching, I hate the reuse of the keyword switch when the term match would make more sense.
@@jfftck match is great. I also like when. Switch never made sense to me.
with angular you can just use ng container and ngIf ngFor without trading in clarity. With the new control flow syntax they may introduced the same issue.
I agree with the top to bottom going into left to right, that is annoying to parse... The linter we use forces all JSX to be wrapped in parens with new line after start/before end. This way it's just reading top to bottom still even with the ternaries. Granted, I also would push back on nested ternaries. I'd rather just put the new ternary in a new component.
Though, I am pretty interested in trying to do the return null flow you show here instead of ternaries in general.
I really like that new formatting. Reads almost like if-elseif-else code. Although I agree that you should avoid nested Ternaries as much as possible. But the next time I run into code where a prior dev has a bunch of nested Ternaries on a single line, I'll happily reformat it to this before trying to read it.
6:47 I love this!
Not being able to read the code is not the ternary's fault
yes, it's so much better to call a function and return null immediately
4:14 whats the logic behind wrapping the arguments into props?
I guess it avoids repeating parameter names (for actual parameters + for types). It also makes it clear on usage when a variable is a prop or not.
I'm glad I found your channel this year. I've been at this game professionally for 16 years now, 28 total, but I usually come away with some great insights from your content. So thank you and I hope you have a wonderful 2024!
I think ternaries with "Clean Code", that is putting names on each path, is readable, but not inside JSX.
Agree especially now that undefined is fine.
just use switch statements?
switch(true) {
case : return stuff
}
I tend to find it more readable if the syntax is fairly terse (not ridiculously terse like array programming languages) and unless you're reusing a function or component; I usually prefer large functions/components over a bunch of small ones. While I've always been prone to write rather large functions; when I was younger I found myself splitting out in to sub-functions a lot more than I do now, these days I will usually only split out sub functions if it makes the code significantly shorter or of it makes scoping easier... I will often even often restructure the code so that I can use fewer functions if possible.
End I wonder if it's not only experience/old-school preferences, but also physical aging. I need bigger fonts to see well these days (I no longer prefer dark mode during daytime either because my eyes need more light); and I've always preferred usning a single and fairly small monitor for coding (too big screen means a lot of wasted white space blasting light at me, or distractions in my peripheral vision if I use non-maximised windows); so with a larger font I have less screen space than before and code is much easier to reason about when you can see all the relevant parts at once. And while ternaries have the disadvantage of using very small symbols; at 18 pt font size they're still quite visible.
And I really can't stand (and I never have) having the logic of some feature split over several files. I despise languages that require you to put each thing in it's own file, because those implicitly requires you to split your logic over several files if there is any code re-use at all.
But I'm sure for the kids who write code in microscopic fonts such as 10 or even 8 pt it could be hard to see a colon since it's a just two faint antialiased blobs. And if you write in microscopic fonts, and maybe even use huge and multiple monitors, maybe having the logic split up into dozens of verbose named functions or components doesn't bother you as much as it bothers me, because you can see the whole content of maybe four 80 line files at once; while I normally can only see around 35 lines of one single file at the time.
and no this ain't some obscure esoteric lang - the function in the screeenshot is POSIX-compliant awk syntax
What we really need is expressions like rust has, like
const x = {
if(something) return 1;
else if(somethingElse) return 2;
return 3;
}
Same as immediately called function, just less awful.
const x =:{something} is way better than const x = (()=>{something})().
Even if it does have a very upset face in the front, it's probably way less upsetting than deep ternaries.
Maybe even omit curly bracers, like
const x =: if(a) return 1; else if(b) return 2; else return 3
or even omit returns.
Though I can already see people doing side effects in this, that would be a mess. Not that you can't do side effects with ternaries, I'm guilty of console logging stuff during jsx render for debugging myself.
I've used them, it's been a pain. Especially whenever there's something recursive to do with a deep type that has to support plain types, objects and arrays. I don't think there's a way around doing
T extends ValueType
? ... // string, bool, number etc
: T extends any[]
? ... // arrays, you can recurse with the single element with T[number]
: ... // objects, do a mapped type or something like that.
Any time I make a new component my job requires me to make a new unit test suite and Storybook story for it so I tend not to make new components if I don't absolutely have to
Ternaries are totally fine if they are used for very simple things like variable assignments.
In JSX I really like the component by SolidJS, you can very easily implement it in react:
function Show({ when, children }) {
if (!when) return null
return children
}
It's sad in 2024 that React doesn't have the equivalent of and from solidjs built in....
I love javascript, truly. I don't like jsx. I just program everything in javascript, html, and css. I like it more that way, and I get to learn how to make things fast by understanding javscript better, instead of muddling through with react and compilation which slows me down too much, honestly. The only thing that really slows me down now, is finding why the NaN value is happening after refactoring, but I'm working on a tool to help with that... Maybe it will be out soon. In the mean time, it's happened so often now that I know what to look for. 9 times out of 10, it happens because of division or other mathematical operations with an undefined variable.
Maybe this is just a me thing, but whenever i shuffle through the "when to use what" and ternaries comes to mind, i make sure that either of my arms is "meaningful". To me, falsy values on either arm are not "meaningful". At least within the context of React, i only use ternaries to render either component A or component B. NEVER whether a component should be rendered or not. There are usually better options at that point.
what about using the nullish coalescing operator (??)?
"&&" operator isn't great in JSX if you aren't explicit about your code. Just use proper length checks such as ".length > 0" or ".length === 0" and just avoid using JS quirks that many devs don't even know how they work. It will also be way easier to read your code because it's super clear what condition you are checking here and not depending on "falsy" quirks of JS. I mean, use explicit checks even outside of JSX, don't try to be super smart about your code, but rather make it clear what are the intentions of that code.
Regarding ternaries, I dislike using ternaries in JSX to render "null" as the "else clause" because it's pointless to me. If I use a ternary in JSX, I want to render something meaningful for both branches and rendering "null" isn't meaningful to me and just introduces unnecessary nesting and dangling nulls in code. I'm ok with using ternaries to render simple markup, but if I get to nested ternaries, I abstract that into some if/elseif/else component as shown in your example.
Scrolling the comment section all the way down. See only one or two comments like yours mentioned about the condition itself.
People are abusing ".length" as a boolean. Why can't we just check/compare the length via ".length > 0"" as the original intention to make it as a proper boolean condition instead of trying to do something like Boolean(length), !!length or what so ever. Just to save few key strokes? This is not something really arguable, lately I really start to doubt the average js dev ability.
@@doc8527 Yeah I never understood why people use ".length" without actually comparing it to something. It just makes code confusing. To me it's just harder to understand at a glance what is the intention of that code and it can also lead to weird cases because people don't actually understand how it works.
6:57 was a flex and I’m offended
I never found nested ternaries good or even elegant. I one of ternary to set a value, based on a condition ok. But nesting them just feels hacky. I always loved early returns since they day I first did that pattern. It just makes so much more sense do go "You are not that? Ok go away!". Just feels like a bouncer at a door that just doesn't let people in when they wouldn't fit. AND you can even optimize performance with having the "easy" checks first. I know that you can do that with ternaries, but it's just so much easier to see when everything is pretty much sequential. With ternaries it has more a this feeling that you have 8 bouncers that are sending the people into different queues that have other bouncers.
If your if-else -rewrite starts to get ugly, the best way is not to just fall back to the old solution, but to think about better structuring what your if-else does, and if you really need to nest that deep. And if you really need to. Better ugly and readable, then fancy and you needing 3 red bulls and a phd to grasp it.
Bri thise mario style meme about it is amazing 😭😂🤣 im crying of laugh
I would have thought using a debugger was on the list of things nested ternaries make harder to do
Idk console.log seems to be working fine
@@GenghisD0ngIMO it's better and much faster to put a breakpoint. You basically get your own steering wheel, pause and inspect each line, giving you better understanding of how a code works without reading the whole program.
I'm a bit concerned that Prettier is trying to justify that nesting ternaries is a good idea. the only case i ever see for a ternary is if you're doing a conditional spread in an object or array and then doing `{ ...myObject, ...(isThisThing ? { field: 'some value' } : { otherField: 'some other value' ) }` because although you can do ` ...( isThisThing && { field: 'some value' } )` that same doesn't work with arrays and you'll have to ternary arrays. So it just keeps that syntax similar.
i do agree that if/else is messy, but that's why doing early returns with if checks is a lot cleaner than if/else blocks. A ternary is used it if it's a dichotomy, so just doing if (!notThing) { return } and then having your other logic continue below for me is a lot easier to read, and can often stop if/else nesting as well. And if it has a lot of conditions that's where switch statements or maps are useful. Because i totally agree having good legibility in your code is key, being able to come back into something months down the line and not feel like you're tiptoeing through cryptic but concise code is a godsend for refactoring, making changes, or just reviewing
For some reason, my team is absolutely obsessed with tenaries. Everything that is just an if else with returns is going to be refactored into tenaries regardless of how applicable it is or not
I think && is just fine if you don't overdo it. I mean if the codepiece is really complex you may outsource it into its own component, but even then i would put the condition into the outer component with &&, since it is really easy to read and understand for me that way.
The more I know about react, the more i like Angular
10:42 C++ entered the chat: "Hold my beer"
Your Typescript error to subscribe to your channel is magnificent!
I believe that programmers, when given the choice, will always choose what is most concise and "easy". In this case, it's the ternary operator versus an if else - but I disagree with the point that they are simpler, as the guy in the future will have much more trouble dealing with a complex ternary operator than a complex if else. And I wholeheartedly believe that having ternaries in a programming language is a bad design decision. Even though there are cases where a ternary operator is better than an if else operator, there are far more cases where a simple and descriptive if else is better. In the 10% of cases that we lose, we win in the 90% that remain. I'm a big proponent of strict and firm ways of doing things. That may be one reason why I like Go and Rust, even if on different ground.
What about the use of ternaries in tailwind classes? For me, I have no problem with it especially when the class depends on state.
I feel that if you let a bunch of developers consciously format these, some better patterns would emerge for different situations.
Not about ternaries, but I think you should never do this kind of check if a value is falsy like "something.length && ..." or "if(a.length)".
It is a tiny bit more work to write out the full condition with "!== 0" but it makes things more clear and more well defined. If youre not careful you will also end up doing weird things you didnt intend to do. For example, if you want to check that a variable str that could be null or a string is not null and you do this implicit check, an empty string would be handled the same way null would be handled. And even if you want to do that, write out two conditions because then at least people will know that both parts of the condition are intended and you didn't implicitly check for multiple conditions by accident.
And these hacks people do with the !! are the weirdest thing. You feel so cool when you shorten your code by 4 characters but all you're really doing is making your code less readable.
An example of implementing ternaries well is Kotlin in my opinion. The language doesn't have the classic ? and :, but instead an if statement is an expression.
You can do something like "val x = if(...) { ... } else { ... }".
It is more concise, allows you to handle cases where you have more than two possibilities in a readable way (else if rather than chained ternaries) and you can also write proper code into the blocks and arent just limited to returning a value.
You can also do !!something.length && ... 😂. Not that readable, but it is now a boolean and not a number anymore xD. 7:53
Ngl I started the video assuming I would disagree considering how popular ternaries are in JS, but I think I agreed on basically everything. I do wish they had the inline syntax that python has with inline If's instead of ternaries since I find that much more readable
I think the only real problem here is bad usage of the tools available.
Ternaries can be perfectly readable in simple assignments or in that specific line where you wrote
```userInfo ? Something : null```
But thinking that you should use a tool "everywhere" like the article suggests, it's just not viable.
All the tools in the programming languages are there to be used whenever you're in need of them, if he used a Ternary ot solve a specific problem, then good for him, but not because of that, you're immediatly going to start using ternaries everywhere and avoid using logical ands.
There's place for everything, and everything goes in its place, so I don't agree at all that ternaries are bad, or that you shouldn't use logical ands, it's just a matter of propperly balancing your use of the many language tools the programming language gives you to write readable and efficient code.
Many languages don't allow early returns from functions and have only the ternary if. Look up "cond" in lisp for an example of this. The syntax you explore later on in the video is really just an approximation of cond. I think once you've seen a language that does ternaries well, you understand that they are better in most cases.
Nested ternaries were extremely common in Verilog before tool support for “if” statements were strong. Look in any legacy Verilog file and you will see the same thing
Sometimes I wonder why people would even need to do a `contacts.length && contacts.map`. The map would simply just not return anything if the list is empty, so it's kinda pointless to check for it.
is this just "make smaller less complex functions" but for components?
Edit: Flatten = T[number] works for me just fine btw :3
Amazing content!
i've been asking and pleading for JSX directives proposal for ages
4:45 but in vue and probably in svelte (i haven't tried svelte yet) you cannot just return null like in react. In those frameworks you always need to define condition in the template.
Seeing the nightmare that is TS type definitions, I personally prefer just writing complex typed code in something else (C++, Rust, etc.) and using WASM FFI to access results. TypeScript is doing the best it can with a bad situation in trying to add typing to language designed not to have it. I'd rather keep the JS/TS small, simple, parsable, etc., and offload the difficult static analysis to a strongly typed language.