[HN Gopher] Bad TypeScript Habits
       ___________________________________________________________________
        
       Bad TypeScript Habits
        
       Author : jgwil2
       Score  : 68 points
       Date   : 2021-02-02 19:22 UTC (3 hours ago)
        
 (HTM) web link (startup-cto.net)
 (TXT) w3m dump (startup-cto.net)
        
       | SmellyPotato22 wrote:
       | I disagree with claim that One letter generics are a bad habit.
       | In my opinion, it is proper to use one letter generics but start
       | with A then B, then C... so it enables to reason about generics
       | positionally. Pseudo example of this:
       | 
       | `map<A,B>(fn:(el:A)=>B):B[]`
        
         | brundolf wrote:
         | Hard disagree. Having come into a TS codebase from the outside
         | that made liberal use of generics, they can make things
         | incredibly difficult to follow if they don't have really good
         | naming and/or docs.
        
           | Garlef wrote:
           | Yes. But do long names even help? I imagine, in such a
           | project the system of generics can be so complex that a new
           | dev cannot use them to their intent without completely
           | understanding the code.
        
             | brundolf wrote:
             | You could say the same thing about variable names:
             | 
             | "But do long names even help? I imagine, in such a project
             | the system of logic can be so complex that a new dev cannot
             | use them to their intent without completely understanding
             | the code."
             | 
             | Meaningful names _help_ a person gain an understanding of
             | the code. And that 's not even getting into whether or not
             | "completely understanding the code" is a tractable goal in
             | the first place (I don't think it is, even for the people
             | who _wrote_ the code, much less those who come later)
        
               | Kranar wrote:
               | Long variable names don't help either. It's been mostly a
               | stylistic choice and a recent trend that stylistically
               | long variable names are nice. But there are plenty of
               | projects that use short variable names or even one letter
               | names, and I don't see any evidence that those projects
               | are less productive because of it.
               | 
               | The actual research is that long variable names make it
               | harder to understand code. It makes the variable name
               | become an integral part of any code snippet that a reader
               | feels compelled to memorize the specific names used, and
               | that memorization results in errors and increased recall
               | time.
               | 
               | Short variable names, including single letter names,
               | allow a reader to basically ignore the name itself which
               | allows them to accurately memorize an entire chunk of
               | code without feeling the need to memorize long names.
               | This improves recall time.
               | 
               | In simpler terms, when a snippet of code uses descriptive
               | variable names your brain is inclined to believe that
               | those names matter and that you need to remember them,
               | compared to short variable names that your brain will not
               | have to bother remembering and instead frees you up to
               | focus on the structure and semantics of the code snippet.
               | 
               | The study that you can read, which is quite insightful is
               | here:
               | 
               | https://pdf.sciencedirectassets.com/271600/1-s2.0-S016764
               | 230...
               | 
               | Note that long function names DO help, but long variable
               | names do not.
               | 
               | That said, feel free to use long variable names if that's
               | what you like and your project does it that way. Don't,
               | however, express that as some kind of extensively studied
               | software engineering principle that has research backing
               | it. It's basically one of those things someone decided
               | was true and everyone else just decided to believe it
               | without ever doing the kind of work needed to validate
               | the hypothesis.
               | 
               | In software engineering, we are still in the days of
               | Aristotle, where we simply believe things to be true
               | because we want them to be true. Just like the days of
               | Aristotle I wouldn't be surprised if it takes us
               | literally thousands of years before someone comes along
               | and decides to question and test basic assumptions we all
               | take for granted.
        
         | jgwil2 wrote:
         | I think I agree with you (though I would favor T, U, V as the
         | sequence).
         | 
         | What would the alternative look like in what you wrote? In and
         | Out? Range and Domain? Generic type variables are often too
         | abstract to give a sensible name to.
         | 
         | This is the one "bad habit" that I think is a bit silly -
         | naming conventions can vary by project.
        
           | SmellyPotato22 wrote:
           | In the context I am thinking about, writing a generic
           | iterator, you have 0 context of the what the concrete types
           | will ever be.
        
         | MrPatan wrote:
         | Do you also use one letter variables and parameters? If not,
         | what is different about generics?
         | 
         | Even in your example, I'd rather read this:
         | 
         | map<IN,OUT>(fn:(el:IN)=>OUT):OUT[]
         | 
         | Plus, it really is a habit. If you stop doing it for these kind
         | of examples that really are very generic, maybe people wouldn't
         | also use only one letter for cases where there is more of a
         | difference between them.
        
           | SmellyPotato22 wrote:
           | Kinda gets a little hairy if you start to add more types.
           | Example: `//fork :: [a] ->((a -> b ), (a -> c)) -> ([b]
           | ,[c])`
           | 
           | `fork<A,B,C>(fn1:(el:Iterator<A>)=>B,fn2:(el:Iterator<A>)=>C)
           | `
        
             | MrPatan wrote:
             | What would be the opposite of "fork", something like "zip"
             | that gets two lists and combines them into one? Would it
             | have a signature also starting like this: `zip<A, B,
             | C>(...`
        
           | legerdemain wrote:
           | > Do you also use one letter variables and parameters?
           | 
           | A question in response to your question... Do you ever give
           | an ALL CAPS name to a class? Why not?
           | 
           | Maybe it's the set of conventions I'm used to, but I find T,
           | U, V familiar and IN, OUT jarring and dramatically less
           | readable at a glance.
        
             | MrPatan wrote:
             | I use ALL_CAPS in generics so they are easy to tell apart
             | from functionNames and ClassNames
        
           | Kranar wrote:
           | Sometimes I do, sometimes I don't. If it's an iterator I
           | often use one letter names such as i, j, k. Similarly if it's
           | a generic unbounded type I also give it a one letter name.
        
       | paxys wrote:
       | Nice article, but all the negatives made it a little hard to
       | read. For some points it was hard to figure out whether the
       | author was in favor of doing or not doing it.
        
       | bengalister wrote:
       | About #2 Wasn't so sure about typescript so I tested it to
       | confirm that it worked. But in Python3, the default arguments are
       | evaluated at module time if I am not mistaken. The following will
       | display roughly the same timestamp                 from datetime
       | import datetime       import time
       | print(datetime.utcnow())       def show_ts(d=datetime.utcnow()):
       | print(d)            time.sleep(5)       show_ts()
       | 
       | where as in TS, the 2nd one will be roughly 5s older:
       | const now = Date.now();       console.log(now);
       | function logTs(d=Date.now()) {          console.log(d);       }
       | setTimeout(()=> {          logTs();       }, 5000);
        
         | horsawlarway wrote:
         | That's because default params in JS are essentially syntactic
         | sugar ex -                 function example(a = 1) {       ...
         | }
         | 
         | behaves the same as                 function example(a) {
         | a = (typeof a !== 'undefined') ?  a : 1       ...       }
        
           | sorahn wrote:
           | Which also means the suggestion to use ?? has a different
           | result then using a default param. ?? will catch both null
           | and undefined, where as a default parameter will only trigger
           | on undefined.
        
         | tempodox wrote:
         | Events that happen at a later time are called "younger", not
         | "older". If you were born at a later date than me, you're
         | younger than me.
        
       | nepeckman wrote:
       | I think #6 (optional properties) is a pretty contrived and
       | unrealistic example. Most people use optional properties for
       | extra options or enhancements, not as substitution for an object
       | hierarchy.
        
         | jgwil2 wrote:
         | I've definitely used them in the way the article describes.
         | Think about an API that gives a list view and then a detail of
         | an object that has more properties than are sent in the list
         | view (maybe because they are expensive to compute or maybe just
         | to save bandwidth). This scenario can definitely be more
         | accurately modeled with interface inheritance than with
         | optional properties.
        
           | nepeckman wrote:
           | Okay your answer is more illustrative to me of the problem
           | and solution. Totally agree, if you've got a list view and
           | detail view those should be separate classes. Though my
           | personal take in that senario is you might be better off
           | using TypeScripts mapped types to define a subset of your
           | detail view as a list view.
        
         | drew-y wrote:
         | Agreed. The author is conflating discriminated unions with
         | optional properties. His example _is_ better off as a
         | discriminated union because the optionals are set based off of
         | it 's type. There are plenty of situations where optional
         | properties do make sense. Particularly where a default value
         | can be used.
        
       | drinchev wrote:
       | Btw in this context I would like to share Type Fast by
       | sindresorhus [1].
       | 
       | I find it excellent and definitely a helpful tool to think about
       | when you are struggling with type safety vs runtime data.
       | 
       | 1: https://github.com/sindresorhus/type-fest
        
       | paxys wrote:
       | Soft disagree with 3. unknown might be better than any but then
       | the typechecker isn't going to let you read any fields from the
       | object, even if you are using a null check. And the given example
       | is weird because you don't need to add a type annotation there at
       | all. The problem really is that TypeScript doesn't have an
       | "unknown deserialized JSON" type.
       | 
       | For #7, in a lot of (most?) cases, the name of a generic isn't
       | meant to be descriptive. I'd argue that functions that expect it
       | to be are doing it wrong. If I have a `function
       | reverseArray<T>(arr: T[])`, the point of T is that it can be
       | anything. Saying `reverseArray<ArrayTypeToBeReversed>` serves no
       | purpose.
       | 
       | TypeScript also really, really needs to have a strict
       | typechecking mode. Yes I know the "it compiles to raw JS"
       | arguments, but it could easily add extra checks in the JS code
       | (basically what example #4 shows).
        
       | deepfriedrice wrote:
       | Good stuff.
       | 
       | I don't get the example for #3. I understand using "unknown"
       | instead of "any", but both examples look effectively the same in
       | terms of type safety.
       | 
       | I also don't get the example for #4. Side effects are the inherit
       | "blind spot" of TypeScript. If you're really worried about your
       | API changing on you, it seems like you'd be better off modeling
       | it in something like JSON schema. Unless maybe your API is
       | trivial.
        
         | [deleted]
        
         | jgwil2 wrote:
         | In the fixed version of #3, the function return type is
         | `Product[]` instead of `any`. The example is further refined in
         | #4.
         | 
         | What type guards are good for is marrying the compile time
         | checks with runtime safety. They allow TSC to infer that in a
         | given code path, the type can be narrowed down to something
         | more specific. This is nice because it lets a single function
         | e.g. handle a few different types without littering your code
         | with `as` or `!`.
        
         | ivan888 wrote:
         | I also don't understand 4. This looks terrible. Why use
         | TypeScript if we have to make very manual assertions about the
         | structure? Isn't that the purpose of TS itself?
        
           | jakelazaroff wrote:
           | TypeScript's type checking is static, so on its own it can't
           | know that values it receives from elsewhere (like JSON coming
           | from a string) is the correct type. The only option is for
           | the programmer to check at runtime, which allows TypeScript
           | to know the type along the code path in which the check
           | succeeds.
        
             | ivan888 wrote:
             | This makes sense; but it still doesn't feel like the right
             | answer. Facilities should exist for runtime type checking
             | based on TS definitions, and syntactic features should
             | enable a 'hard check' of the actual object when necessary
        
               | jakelazaroff wrote:
               | That runs counter to one of the goals of TypeScript,
               | which is to make no runtime changes.
               | 
               | It's easy enough to do this in userland. I wrote a tiny
               | library called narrows which has worked great for me:
               | https://www.npmjs.com/package/narrows
        
               | ivan888 wrote:
               | Cool library, the type guards look interesting. Too bad
               | it's not more automatic but yeah it's not very much extra
               | code
        
               | curtisf wrote:
               | There are libraries that do type-safe schema validation
               | (an article I found in a quick Google is linked below).
               | 
               | That you can do something so sophisticated is a testament
               | to TypeScript's type system.
               | 
               | However, if you were writing JavaScript, I think it's
               | (unfortunately) uncommon to have this kind of proactive
               | validation. So, for people writing TypeScript as
               | "JavaScript + types" (which is a perfectly fine way to
               | use it), they would just use a type assertion, which is
               | just writing down the assumption that the JavaScript
               | programmer makes when consuming but not validating
               | external data.
               | 
               | https://2ality.com/2020/06/validating-data-
               | typescript.html#p...
        
         | [deleted]
        
       | Garlef wrote:
       | Regarding "don't use `x as Y` but instead use type guards".
       | 
       | Type guards have a runtime overhead, don't they?
       | 
       | (You could argue: "If TS can not infer the type at this point so
       | neither can you. Better to check at runtime." But from my
       | experience, sometimes TS is simply not smart enough.)
        
         | throwanem wrote:
         | Type guards do have a runtime overhead (since they're functions
         | that get called), but so does any other method of validating
         | untyped data at runtime. That's probably the 99% case with type
         | guards, so I feel like the specific overhead on that isn't such
         | a huge deal since anything else (e.g. runtypes) would probably
         | have more.
         | 
         | (I can think offhand of one case in the last year where I've
         | wanted to use a type guard for a reason that _didn 't_ have to
         | do with runtime validation, anyway. Even then, it wasn't the
         | solution I ended up going with.)
        
           | Garlef wrote:
           | Out of interest: What was the context where you needed the
           | validation?
           | 
           | In most contexts, the reasonable approach would be to only do
           | runtime validation at your system boundaries and not anywhere
           | inbetween.
        
             | throwanem wrote:
             | The case I mentioned didn't have to do with validation - in
             | a case like that, I _would_ have used a type guard, or more
             | likely a runtype. This was a Javascript corner case that I
             | hadn 't previously encountered, where if you load the same
             | module from two different paths, they aren't
             | `instanceof`-equivalent. In this case, both the codebase of
             | a module I was working on, and the codebase of one of its
             | consumers, imported the same module from the same package,
             | and I ran into the issue when I `npm link`-ed the module
             | into its consumer's node_modules dir for testing and to
             | ensure I hadn't inadvertently broken an implicit contract.
             | The `instanceof` mismatch, between an object instantiated
             | in module code and the class imported by the consumer,
             | confused the type system and gave me spurious errors as a
             | result.
             | 
             | Thinking about how to solve this, it occurred to me to use
             | a type guard, since it would have effectively convinced the
             | type checker that my object instance was in fact an
             | instance of the class of which it is an instance. But it
             | would've been a wart and also imposed the aforementioned
             | runtime overhead - not that that would've been meaningful
             | in this case, it wasn't in a hot loop or anything, but I
             | still didn't like it. So I ended up instead exporting the
             | shared dependency from the module and having the consumer
             | import it from there, rather than from its own (IIRC
             | otherwise unused and thus eliminated) copy of the shared
             | dependency.
        
         | adamredwoods wrote:
         | Don't we mostly see this from json responses? The example they
         | gave adds overhead, checking each product form an array. We use
         | some responses with 200 products, we don't want this extra
         | latency.
         | 
         | Does JS need a better way to introduce json type safety? C# and
         | graphql use schemas, I wonder if this is a better approach
         | (compile time, not run time).
        
           | Garlef wrote:
           | If you're receiving JSON data, you can not validate the data
           | at compile time.
           | 
           | But yes: I think TS is missing a way to derive runtime
           | functionality from the type definitions - at least in the
           | cases, where the types describe plain JSON.
           | 
           | Something along the lines of clojures' `spec`.
        
             | jakelazaroff wrote:
             | You can do the converse: create a type guard asserting a
             | structure, and then extract a type.                 type
             | Spec<T> = T extends (x: unknown) => x is infer U ? U :
             | never;       type Foo = Spec<typeof someTypeGuard>;
        
           | [deleted]
        
       | recursive wrote:
       | Disagree on #10. The amount of code I've seen that intentionally
       | distinguishes between null and undefined is... well, zero. On the
       | other hand, the amount of code I've seen that unintentionally
       | distinguishes between them when it shouldn't have is not zero.
        
       | izolate wrote:
       | I like the "why we do it" part in every section. Kudos to the
       | author for that.
       | 
       | However, the type guard section seems a bit off to me. Is the
       | suggestion to check each and every property of `Product` in
       | `isProduct`? Seems a bit verbose.
       | 
       | I tend to use Axios, which uses generics to set the payload
       | return type (obviously, some trust in your API responses is
       | needed here):                   axios.get<Product[]>(...);
        
         | sakarisson wrote:
         | I think the idea is that you validate the data once, on an API
         | level. You don't need to validate the same data again after the
         | initial inspection.
         | 
         | In our team we use io-ts to validate all of our endpoints, but
         | simple type guards could achieve the same goal.
         | 
         | https://github.com/gcanti/io-ts
        
           | adriancooney wrote:
           | Runtypes is another _excellent_ alternative. Highly
           | recommend.
           | 
           | https://github.com/pelotom/runtypes
        
         | horsawlarway wrote:
         | To me - A type guard lets me be as picky as I'd like to be in
         | the given circumstance.
         | 
         | I also use axios, and I also take advantage of the generics
         | you've shown above, but I acknowledge that I'm essentially just
         | saying "Trust me" and doing a cast when I do that.
         | 
         | And I'll add - that exact style of code has been a source of
         | bugs in our production codebase before. A dev will pick the
         | wrong type for the axios generic, and if the api response
         | happens to overlap on the used fields, no one notices. Then it
         | blows up 6 months later when someone tries to access a field on
         | the defined type that wasn't actually returned in the api
         | response.
        
         | agloeregrets wrote:
         | The word in the back of my head wasn't Verbose. It was
         | `Expensive`. Explicit looping and checking values like that
         | seems like the answer to 'why is our app slow'.
        
           | horsawlarway wrote:
           | I mostly disagree with you here.
           | 
           | Checking for the existence of a key on an object is dirt
           | cheap.
           | 
           | Even if you happen to have a case where it is expensive,
           | there's absolutely nothing that says your typeguard has to
           | take that approach.
           | 
           | I've seen some reasonably sane code that just checks that the
           | 'Type' field of the object matches the expected value, and
           | the 'Version' field is the right number. It's not going to
           | catch all the possible errors there if someone breaks the api
           | contract, but it's a lot better than a raw cast.
        
           | bwestergard wrote:
           | The latency will be linear with the size of the JSON payload,
           | and the constants will be tiny.
        
       | llimos wrote:
       | 11. Going down the rabbit hole spending three whole days getting
       | your typings perfect for some weird use case, instead of writing
       | actual code. _Sometimes_ there 's a benefit down the line to
       | having done it, often not, and knowing the difference is where
       | greatness lies.
        
         | Madeindjs wrote:
         | I completely agree with you. I once use JSdoc instead of
         | Typescript for a small project. Typing works as good as
         | Typescript on VS Code.
        
           | keyle wrote:
           | This!
           | 
           | I found it far more productive that going through rabbit hole
           | of TS and its compiler. I write my `definitions.d.ts` for my
           | objects, and use them simply                  /** @type
           | {ns.MyType} identifier */        const identifier...
           | 
           | and voila, my IDE gives me auto-completes and warns me when
           | I'm doing silly stuff.
           | 
           | It works for functions as well using                  /**
           | @param {type} parameter name */
           | 
           | and is generally a good idea to use.
           | 
           | It gets you 80% of the way there with no compiler/transpiler
           | (although one might argue that the IDE is compiling non-stop
           | on file saves, with its language server).
           | 
           | I embraced Typescript when it was new and the Javascript
           | standard was a total mess. It was leaps and bounds ahead and
           | closer to something like AS3. At the time TS was godsend.
           | 
           | Today, JS + webpack gets you really far using the latest
           | ECMAscript standards.
           | 
           | Same with IDE. The amount of stuff my IDE can deduct based on
           | my few typings and my use of modern strict javascript has
           | strongly reduced the need for another transpiler (on top of
           | webpack).
           | 
           | I found jsDoc to be a perfect 80-20 solution.
           | 
           | Because at the end of the day, TS isn't a compiler, it's a
           | transpiler. You're not compiling to bytecode in a vm, you're
           | still in javascript.
           | 
           | This saves my bacon in the front-end. For the back-end, I
           | most definitely choose a strongly typed language like Go.
        
         | jamil7 wrote:
         | I kind of came to the same conclusion at some point. I normally
         | find statically typed languages much more productive. If you're
         | working with all typescript libraries with great typings it's
         | probably great but the reality of the javascript ecosystem
         | means you're often wasting time getting the compiler to
         | understand how a few dependencies should cooperate. That and
         | the fact that I contracted of a few larger projects that were
         | in an transition phase filled with 'any' which often meant
         | little type safety but a lot of extra typing.
        
         | Garlef wrote:
         | This is definitely something that typescript constantly lures
         | you into.
         | 
         | I recently switched back to JS for a prototype of a programming
         | language.
         | 
         | I immediately felt more productive.
         | 
         | At times, the TS systems feels like a theorem prover. Until it
         | doesn't. And then you're left with a bunch of types that
         | _almost_ do what you want... but not quite; Always tempted to
         | experiment again for a day or two.
         | 
         | But let's see what happens down the road. Maybe I'll miss the
         | incidental documentation the typings provide.
        
         | acemarke wrote:
         | Absolutely. One of my strongest opinions about TS is that you
         | should use it pragmatically. It's not worth burning hours of
         | time trying to placate the TS compiler if you know the code
         | works okay - throwing in a `type $FixTypeLater = any` and
         | moving on is an acceptable workaround depending on the
         | situation:
         | 
         | https://blog.isquaredsoftware.com/2019/11/blogged-answers-le...
        
         | MattGaiser wrote:
         | I have been working on a frontend project for the past two
         | weeks now. Probably about half my time has been dealing with
         | type errors.
        
       | calebegg wrote:
       | I think calling your generic type 'Element' is a bad choice --
       | that's shadowing a built-in type
       | (https://developer.mozilla.org/en-US/docs/Web/API/Element) and it
       | makes the code confusing to read at a glance.
       | 
       | That's one thing I like about T, it's very self evident that it's
       | just 'the' generic type. But maybe I'm just used to it. With
       | multiple generic types I can see how longer names would be
       | useful.
        
         | hooksfordays wrote:
         | Agreed. I think naming generic types makes the most sense with
         | multiple types, or when more specific names make sense. For
         | example, if a generic argument must inherit from a certain
         | type: `class SomeView<ViewState extends BaseViewState>` or some
         | such.
         | 
         | `Element` is as non-descriptive as `T` in the author's example
         | and, IMO, doesn't add any tangible benefit.
        
       | elnygren wrote:
       | Good points!
       | 
       | Seems like for "6. Optional properties"
       | 
       | One should rather use tagged unions (see: Abstract Data Types,
       | variants) that are usually written like this in TS:
       | type Product =           | { type: 'digital', id: string,
       | sizeInMb: number }          | { type: 'physical', id: string,
       | weightInKg: number }
       | 
       | At least I find it more elegant, concise and fun to work with :)
        
         | bengalister wrote:
         | According to
         | https://github.com/microsoft/TypeScript/wiki/Performance#pre...
         | using union types in your example vs interfaces like #6 is bad
         | for compilation performance.
        
         | agloeregrets wrote:
         | I like this way more. Wayyy more.
        
         | elnygren wrote:
         | Also, for using type guards to validate incoming data, check
         | out https://github.com/pelotom/runtypes.
         | 
         | Or if brave enough to dive into the deep end of FP, io-ts is
         | nice. https://github.com/gcanti/io-ts
        
       | dbartholomae wrote:
       | Hi everyone! Happy to see my article shared here. If there are
       | any questions, just let me know. And yes, the list is opinionated
       | ;) Especially the question on one-letter generic types has been
       | debated a lot.
        
       | drenvuk wrote:
       | I think using it would be the first one.
       | 
       | I don't think I'll ever be able to convert to using typescript,
       | I'd rather have validators running in the code checking external
       | inputs for the proper fields at runtime before the rest of the
       | system blithely processes whatever it gets due to safe
       | assumptions. Add integration test for specific modules and
       | everything works fine.
       | 
       | I don't want to compile languages that don't need compilation and
       | I don't like the fact that I need to create all of these fake
       | feeling types which are actually just objects rather than
       | javascript classes. It's just feels like boilerplate when you
       | could instead include the 'type' that it's supposed to be in the
       | name and look up a validator for that 'type'.
       | 
       | I'm probably wrong, but it really doesn't feel like it.
        
         | koolba wrote:
         | Static typing check is a huge boost for code refactoring. It
         | allows you to quickly make changes and be reasonably confident
         | that you haven't horrendously broken something.
         | 
         | It doesn't obviate the need for dynamic validation nor is it
         | meant to do that. But it does give you a clear point of where
         | to perform those validations rather than littering it
         | everywhere throughout your application.
         | 
         | It doesn't obviate the need for testing either. But it does
         | eliminate an entire class of dumb programming errors such as
         | incoming a function with the wrong arguments.
        
         | throwanem wrote:
         | Yeah, I mean I don't want to just say "you're wrong", but you
         | kind of are here.
         | 
         | TypeScript alone doesn't make any assertions and doesn't offer
         | any checking at system boundaries ("external inputs"). That's
         | not what it is for, and the types you specify with it aren't
         | objects. All that stuff gets compiled out before the code is
         | even run. The point of TypeScript is so that you don't _have_
         | to run your code to know whether the way it handles data
         | internally is the way you intend it to.
         | 
         | Validation at system boundaries is a separate concern, and
         | handled separately. I like to do it with a package called
         | "runtypes", which both supports arbitrarily complex shapes in
         | its validators, and expresses them in a way that's very close
         | to TS and can be trivially converted into compile-time TS types
         | so that you don't have to write the same types twice.
         | 
         | I wasn't sure what to make of TypeScript for a while, too. But
         | having once tried it, and since become very conversant with it,
         | I'll go back only for trivial tasks or under significant
         | duress. Sure, writing types is a fair bit more work up front
         | and a little more ongoing, but it really does make a huge
         | difference in maintainability; in my experience, it's a little
         | work now to save a _lot_ of work later, as the resulting code
         | is both less likely to exhibit an entire class of often quite
         | subtle bugs, and easier to reason about and modify.
        
       | h1fra wrote:
       | I think the code sample after `Use the new ?? operator, or, even
       | better, define the fallback right at the parameter level.` is not
       | correct, it stops after new Date without any mention of ??
       | 
       | edit: read too fast :facepalm:
        
         | dbartholomae wrote:
         | It uses a fallback at parameter level instead of the ??
         | operator.
        
           | h1fra wrote:
           | oops indeed, but I'm not crazy there was a missing
           | parathensis no? It's too late :D
           | 
           | Anyway great blog post
        
             | dbartholomae wrote:
             | Yes, someone found the typo, send it to me via Twitter, and
             | I fixed it. This is also where I know from that the article
             | is on Hacker News :D
        
         | ulucs wrote:
         | It's defined at the parameter level, using function parameter
         | defaults
        
         | jgwil2 wrote:
         | Yeah, the example just shows the "even better" option: it gives
         | a default value for the date parameter.
        
         | [deleted]
        
       | agloeregrets wrote:
       | As much as I agree with most of this, I also think that some of
       | it can lead to some weird issues for new devs or dangerously,
       | lead to a world where people are helpless once they accidentally
       | break out of the world of typescript. In a few cases, there is
       | actual runtime overhead for the choices made, for example the
       | typechecking example.
        
       | dlbucci wrote:
       | I agree with most of these, but I pretty strongly disagree with
       | #10 (don't use `!= null`). The reasoning is that you can use
       | `null` and `undefined` to mean different things, like `null`
       | being "no first name" and `undefined` meaning "haven't asked
       | yet". I agree that strict TS lets you do stuff like that fairly
       | easily, but I think this is a really bad idea, because there's
       | nothing that would indicate to a reader of that code the meaning
       | of `null` vs `undefined` for those properties.
       | 
       | I feel in that case you should have some type union of
       | `"notAsked" | "none" | { t: "some", v: "Name" }`, because that
       | would be more clear of what each possible value means. Let `null`
       | and `undefined` be synonyms for `nil`.
        
         | codefined wrote:
         | I've been regularly trying to reduce / remove usage of `null`
         | in various projects. Sindresorhus has some interesting
         | discussions on the topic here[0].
         | 
         | [0] https://github.com/sindresorhus/meta/discussions/7
        
       ___________________________________________________________________
       (page generated 2021-02-02 23:00 UTC)