惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
S
Schneier on Security
The Hacker News
The Hacker News
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
I
Intezer
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Schneier on Security
Schneier on Security
Security Latest
Security Latest
AWS News Blog
AWS News Blog
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
有赞技术团队
有赞技术团队
博客园 - 叶小钗
The Last Watchdog
The Last Watchdog
O
OpenAI News
月光博客
月光博客
Hacker News: Ask HN
Hacker News: Ask HN
阮一峰的网络日志
阮一峰的网络日志
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
云风的 BLOG
云风的 BLOG
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Latest news
Latest news
P
Palo Alto Networks Blog
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
Google DeepMind News
Google DeepMind News
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
Apple Machine Learning Research
Apple Machine Learning Research
U
Unit 42
PCI Perspectives
PCI Perspectives
博客园 - 聂微东
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
Forbes - Security
Forbes - Security
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Troy Hunt's Blog
博客园 - 三生石上(FineUI控件)
Application and Cybersecurity Blog
Application and Cybersecurity Blog
罗磊的独立博客
WordPress大学
WordPress大学
D
Darknet – Hacking Tools, Hacker News & Cyber Security

oida.dev | TypeScript, Rust

TypeScript's `erasableSyntaxOnly` Flag Unsafe for work Tokio: Macros Tokio: Channels Tokio: Getting Started Network Applications on the Tokio Stack Remake, Remodel, Reduce. The `never` type and error handling in TypeScript 5 Inconvenient Truths about TypeScript Refactoring in Rust: Introducing Traits Refactoring in Rust: Abstraction with the Newtype Pattern Announcing the TypeScript Cookbook TypeScript: Iterating over objects The road to universal JavaScript 10 years of oida.dev Rust: Tiny little traits The TypeScript converging point Getting started with Rust Introducing Slides and Coverage TypeScript: The humble function overload TypeScript + React: Children types are broken TypeScript: In defense of any Rust: Enums to wrap multiple errors Dissecting Deno Error handling in Rust TypeScript: Unexpected intersections Upgrading Node.js dependencies after a yarn audit TypeScript: Array.includes on narrow types TypeScript + React: Typing Generic forwardRefs shared, util, core: Schroedinger's module names Learning Rust and Go TypeScript: Narrow types in catch clauses TypeScript: Low maintenance types Tidy TypeScript: Name your generics Tidy TypeScript: Avoid traditional OOP patterns Tidy TypeScript: Prefer type aliases over interfaces Tidy TypeScript: Prefer union types over enums My new book: TypeScript in 50 Lessons Go Preact! ❤️ this in JavaScript and TypeScript TypeScript and ECMAScript Modules TypeScript + React: Why I don't use React.FC TypeScript + React: Component patterns TypeScript: Augmenting global and lib.dom.d.ts Vite with Preact and TypeScript TypeScript: Union to intersection type 11ty: Generate Twitter cards automatically Are large node module dependencies an issue? TypeScript: Variadic Tuple Types Preview TypeScript: Improving Object.keys Remake, Remodel. Part 4. TypeScript + React: Typing custom hooks with tuple types TypeScript: Assertion signatures and Object.defineProperty TypeScript: Check for object properties and narrow down type Boolean in JavaScript and TypeScript void in JavaScript and TypeScript Symbols in JavaScript and TypeScript Why I use TypeScript TypeScript + React: Extending JSX Elements TypeScript: Validate mapped types and const context TypeScript: Match the exact object shape TypeScript: The constructor interface pattern Streaming your Meetup - Part 4: Directing and Streaming with OBS Streaming your Meetup - Part 3: Speaker audio Streaming your Meetup - Part 2: Speaker video Streaming your Meetup - Part 1: Basics and Projector TypeScript and React Guide: Added a new styles chapter TypeScript and React Guide: Added a new render props chapter TypeScript and React: Styles and CSS TypeScript and React TypeScript and React Guide: Added a new prop types chapter TypeScript without TypeScript -- JSDoc superpowers TypeScript: Mapped types for type maps JAMStack vs serverless web apps The Unsung Benefits of JAMStack Sites TypeScript: Ambient modules for Webpack loaders My most favourite talks in 2018 TypeScript and React Guide: Added a new context chapter TypeScript: Built-in generic types TypeScript: Type predicates JSX is syntactic sugar TypeScript and React Guide: Added a new hooks chapter Getting your CfP application right FAQ on our Angular Connect Talk: Automating UI development TypeScript and Substitutability Debugging Node.js apps in TypeScript with Visual Studio Code From Medium: Deconfusing Pre- and Post-processing From Medium: PostCSS misconceptions Saving and scraping a website with Puppeteer Cutting the mustard - 2018 edition Wordpress as CMS for your JAMStack sites My most favourite podcast episodes in 2017 My most favourite talks in 2017 My most favourite books in 2017 The Best Request Is No Request, Revisited Not so hidden figures - Organizing ScriptConf My podcast journey to ScriptCast Grid layout, grid layout everywhere! #scriptconf and #devone Object streams in Node.js
How not to learn TypeScript
2022-01-10 · via oida.dev | TypeScript, Rust

“TypeScript and I are never going to be friends”. Oh wow, how often have I heard this phrase? Learning TypeScript, even in 2022, can be frustrating it seems. And for so many different reasons. People who write Java or C# and find out things are working differently than they should. Folks who have done JavaScript most of their time and are being screamed at by a compiler. Here are some mistakes I’ve seen people do when getting started with TypeScript. I hope they’re helpful to you!

This article has been very much influenced by Denys’ How not to learn Rust which I can highly recommend.

Mistake 1: Ignore JavaScript #

TypeScript is a superset of JavaScript and has been advertised like this ever since. What this means is that JavaScript is very much part of the language. All of it. Choosing TypeScript does not give you a free card to ditch JavaScript and its erratic behavior. But TypeScript makes it easier to understand it. And you can see JavaScript breaking through everywhere.

See my blog post on error handling for example. It would be very reasonable to allow catching for an error like you’re used to from other programming languages:

try {
// something with Axios, for example
} catch(e: AxiosError) {
// ^^^^^^^^^^ Error 1196 💥
}

But this is not possible. And the reason is because of how JavaScript errors work (check the respective article for more details). Code that would make sense in TypeScript, but isn’t doable in JavaScript.

Another example, using Object.keys and expecting simple property access is also something you would expect, but will cause problems.

type Person = {
name: string, age: number, id: number,
}
declare const me: Person;

Object.keys(me).forEach(key => {
// 💥 the next line throws red squigglies at us
console.log(me[key])
})

There is a way to patch this behavior as detailled here, but this patch can’t be applied to each and every scenario. TypeScript just can’t guarantee based on your code that the types for this property access will be the ones you’d expect. Code that runs perfectly fine in JavaScript alone, but which is hard to express with the type system for so many reasons.

If you’re learning TypeScript with no JavaScript background whatsoever, start learning to differentiate between JavaScript and the type system. Also, learn to search for the right things. Named parameters in functions. You can do that with objects as arguments. A nice pattern. It’s part of JavaScript, though. Conditional chaining? Implemented in the TypeScript compiler first, but it’s also a JavaScript feature. Classes and extending existing classes? JavaScript. Private class fields? You know, the ones with the # in front of them, a little fence so nobody can access what’s behind it. Also JavaScript.

Program code that actually does something is most of the time in the JavaScript camp. If you are using types to express intent and contracts, you’re in type land.

Recently the TypeScript website has a much clearer statement on what it means to use TypeScript: TypeScript is JavaScript with syntax for types. It’s right here. TypeScript is JavaScript. Understanding JavaScript is key to understanding TypeScript.

Mistake 2: Annotate everything #

A type annotation is a way to explicitly tell which types to expect. You know, the stuff that was very prominent in other programming languages, where the verbosity of StringBuilder stringBuilder = new StringBuilder() makes sure that you’re really, really dealing with a StringBuilder. The opposite is type inference, where TypeScript tries to figure out the type for you. let a_number = 2 is of type number.

Type annotations are also the most obvious and visible syntax difference between TypeScript and JavaScript.

When you start learning TypeScript, you might want to annotate everything to express the types you’d expect. This might feel like the obvious choice when starting with TypeScript, but I implore you to use annotations sparingly and let TypeScript figure out types for you. Why? Let me explain what a type annotation actually is.

A type annotation is a way for you to express where contracts have to be checked. If you add a type annotation to a variable declaration, you tell the compiler to check if types match during the assignment.

type Person = {
name: string,
age: number
}

const me: Person = createPerson()

If createPerson returns something that isn’t compatible with Person, TypeScript will error. Do this is if you really want to be sure that you’re dealing with the right type here.

Also, from that moment on, me is of type Person, and TypeScript will treat it as a Person. If there are more properties in me, e.g. a profession, TypeScript won’t allow you to access them. It’s not defined in Person.

If you add a type annotation to a function signature’s return value, you tell the compiler to check if types match the moment you return that value.

function createPerson(): Person {
return { name: "Stefan", age: 39 }
}

If I return something that doesn’t match Person, TypeScript will error. Do this if you want to be completely sure that you return the correct type. This especially comes in handy if you are working with functions that construct big objects from various sources.

If you add a type annotation to a function signature’s parameters, you tell the compiler to check if types match the moment you pass along arguments.

function printPerson(person: Person) {
console.log(person.name, person.age)
}

printPerson(me)

This is in my opinion the most important, and unavoidable type annotation. Everything else can be inferred.

type Person = {
name: string,
age: number
}

// Inferred!
// return type is { name: string, age: number }
function createPerson() {
return { name: "Stefan", age: 39}
}

// Inferred!
// me is type of { name: string, age: number}
const me = createPerson()

// Annotated! You have to check if types are compatible
function printPerson(person: Person) {
console.log(person.name, person.age)
}

// All works
printPerson(me)

Always use type annotations with function parameters. This is where you have to check your contracts. This is not only a lot more convenient, it also comes with a ton of benefits. You get e.g. polymorphism for free.

type Person = {
name: string,
age: number
}

type Studying = {
semester: number
}

type Student = {
id: string,
age: number,
semester: number
}

function createPerson() {
return { name: "Stefan", age: 39, semester: 25, id: "XPA"}
}

function printPerson(person: Person) {
console.log(person.name, person.age)
}

function studyForAnotherSemester(student: Studying) {
student.semester++
}

function isLongTimeStudent(student: Student) {
return student.age - student.semester / 2 > 30 && student.semester > 20
}

const me = createPerson()

// All work!
printPerson(me)
studyForAnotherSemester(me)
isLongTimeStudent(me)

Student, Person and Studying have some overlap, but are unrelated to each other. createPerson returns something that is compatible with all three types. If we’d have annotated too much, we would need to create a lot more types and a lot more checks than necessary, without any benefit.

When learning TypeScript, not relying too much on type annotations also gives you a really good feel of what it means to work with a structural type system.

Mistake 3: Mistake types for values #

TypeScript is a super-set of JavaScript, which means it adds more things to an already existing and defined language. Over time you learn to spot which parts are JavaScript, and which parts are TypeScript.

It really helps to see TypeScript as this additional layer of types upon regular JavaScript. A thin layer of meta-information, which will be peeled off before your JavaScript code runs in one of the available runtimes. Some people even speak about TypeScript code “erasing to JavaScript” once compiled.

TypeScript being this layer on top of JavaScript also means that different syntax contributes to different layers. While a function or const creates a name in the JavaScript part, a type declaration or an interface contributes a name in the TypeScript layer. E.g.

// Collection is in TypeScript land! --> type
type Collection<T> = {
entries: T[]
}

// printCollection is in JavaScript land! --> value
function printCollection(coll: Collection<unknown>) {
console.log(...coll.entries)
}

We also say that names or declarations contribute either a type or a value. Since the type layer is on top of the value layer, it’s possible to consume values in the type layer, but not vice versa. We also have explicit keywords for that.

// a value
const person = {
name: "Stefan"
}

// a type
type Person = typeof person;

typeof creates a name available in the type layer from the value layer below.

It gets irritating when there are declaration types that create both types and values. Classes for instance can be used in the TypeScript layer as a type, as well as in JavaScript as a value.

// declaration
class Person {
name: string

constructor(n: string) {
this.name = n
}
}

// value
const person = new Person("Stefan")

// type
type PersonCollection = Collection<Person>

function printPersons(coll: PersonCollection) {
//...
}

And naming conventions trick you. Usually, we define classes, types, interfaces, enums, etc. with a capital first letter. And even if they may contribute values, they for sure contribute types. Well, until you write uppercase functions for your React app, at least.

If you’re used to using names as types and values, you’re going to scratch your head if you suddenly get a good old TS2749: ‘YourType’ refers to a value, but is being used as a type error.

type PersonProps = {
name: string
}

function Person({ name }: PersonProps) {
return <p>{name}</p>
}

type Collection<T> = {
entries: T
}

type PrintComponentProps = {
collection: Collection<Person> // ERROR!
// 'Person' refers to a value, but is being used as a type
}

This is where TypeScript can get really confusing. What is a type, what is a value, why do we need to separate this, why doesn’t this work like in other programming languages? Suddenly, you see yourself confronted with typeof calls or even the InstanceType helper type, because you realize that classes actually contribute two types (shocking!).

So it’s good to understand what contributes types, and what contributes value. What are the boundaries, how and in which direction can we move, and what does this mean for your typings? This table, adapted from the TypeScript docs, sums it up nicely:

Declaration type Type Value
Class X X
Enum X X
Interface X
Type Alias X
Function X
Variable X

When learning TypeScript, it’s probably a good idea to focus on functions, variables, and simple type aliases (or interfaces, if that’s more your thing). This should give you a good idea about what happens in the type layer, and what happens in the value layer.

Mistake 4: Going all-in in the beginning #

We’ve spoken a lot about what mistakes somebody can make coming to TypeScript from a different programming language. To be fair, this has been my bread and butter for quite a while. But there’s also a different trajectory: People who have written plenty of JavaScript, suddenly being confronted with another, sometimes very annoying tool.

This can lead to very frustrating experiences. You know your codebase like the back of your hand, suddenly a compiler is telling you that it doesn’t understand things left and right and that you’ve made mistakes even though you know that your software will work.

And you wonder how everyone can even like this bugger. TypeScript is supposed to help you be productive, but then all it does is throw distracting, red squigglies under your code.

We’ve all been there, haven’t we?

And I can relate to that! TypeScript can be very loud, especially if you “just switch it on” in an existing JavaScript codebase. TypeScript wants to get a sense of your entire application, and this requires you to annotate everything so contracts align. How cumbersome.

If you come from JavaScript, I’d say you should make use of TypeScript’s gradual adoption features. TypeScript has been designed to make it as easy for you to just adopt a little bit, before going all-in:

  1. Take parts of your application and move them to TypeScript, rather than moving everything. TypeScript has JavaScript interoperability (allowJS)
  2. TypeScript emits compiled JavaScript code even when TypeScript finds errors in your code. You have to turn off code emitting explicitly using the noEmitOnError flag. Not turning it on allows you to still ship even though your compiler screams at you.
  3. Use TypeScript by writing type declaration files and importing them via JSDoc. This is a good first step in getting more information on what’s happening inside your codebase.
  4. Use any everywhere it would be too overwhelming or too much effort. Contrary to popular beliefs, using any is absolutely ok, as long as it is used explicitly

Check out the tsconfig reference to see which configuration flags are available. TypeScript has been designed for gradual adoption. You can use as many types as you like. You can leave big parts of your application in JavaScript, and this should definitely help you get started.

When learning TypeScript as a JavaScript developer, don’t ask too much from yourself. Try to use it as inline documentation to reason better about your code, and extend/improve upon that.

Mistake 5: Learn the wrong TypeScript #

Again, very inspired by How not to learn Rust. If your code needs to use one of the following keywords, you’re probably either in the wrong corner of TypeScript, or much further than you want to be:

  • namespace
  • declare
  • module
  • <reference>
  • abstract
  • unique

This doesn’t mean that those keywords don’t contribute something very important and are necessary for a variety of use-cases. When learning TypeScript, you don’t want to work with them at the beginning, though.

And that’s it! I’m curious on how you learned TypeScript and what obstacles you hit when starting out. Also, do you know of other things that might be common mistakes when learning TypeScript? Let me know! I’m eager to hear your stories.

Related Articles