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

推荐订阅源

大猫的无限游戏
大猫的无限游戏
K
Kaspersky official blog
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog
aimingoo的专栏
aimingoo的专栏
M
MIT News - Artificial intelligence
小众软件
小众软件
云风的 BLOG
云风的 BLOG
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Hugging Face - Blog
Hugging Face - Blog
S
SegmentFault 最新的问题
Stack Overflow Blog
Stack Overflow Blog
量子位
S
Secure Thoughts
G
GRAHAM CLULEY
C
CXSECURITY Database RSS Feed - CXSecurity.com
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
T
Threat Research - Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
G
Google Developers Blog
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
有赞技术团队
有赞技术团队
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Martin Fowler
Martin Fowler
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 聂微东
宝玉的分享
宝玉的分享
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
Netflix TechBlog - Medium
Forbes - Security
Forbes - Security
Engineering at Meta
Engineering at Meta
S
Security Affairs
Help Net Security
Help Net Security
博客园 - 三生石上(FineUI控件)
AWS News Blog
AWS News Blog
博客园 - 叶小钗
Recent Commits to openclaw:main
Recent Commits to openclaw:main
V2EX - 技术
V2EX - 技术
Hacker News: Ask HN
Hacker News: Ask HN
Project Zero
Project Zero
H
Heimdal Security Blog
W
WeLiveSecurity
C
Check Point Blog

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 How not to learn TypeScript 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: 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
TypeScript without TypeScript -- JSDoc superpowers
2019-07-16 · via oida.dev | TypeScript, Rust

One way to think about TypeScript is as a thin layer around JavaScript that adds type annotations. Type annotations that make sure you don’t make any mistakes. The TypeScript team worked hard on making sure that type checking also works with regular JavaScript files. TypeScript’s compiler (tsc) as well as language support in editors like VSCode give you a great developer experience without any compilation step. Let’s see how.

Table of contents #

  • TypeScript with JSDoc Annotations
  • Activating reports
  • Inline types
  • Defining objects
  • Defining functions
  • Importing types
  • Working with generics
  • Enums
  • typeof
  • extending and augmenting from classes

TypeScript with JSDoc Annotations #

In the best case, TypeScript finds out types on its own by infering correctly from the way you use JavaScript. 

function addVAT(price, vat) {
return price * (1 + vat) // Oh! You add and mulitply with numbers, so it's a number
}

In the example above, we mulitply values. This operation is only valid for type number. With this information, TypeScript knows that the return value of addVAT will be of type number.

To make sure the input values are correct, we can add default values:

function addVAT(price, vat = 0.2) { // great, `vat`is also number!
return price * (1 + vat)
}

But type inference just can get so far. We can provide more information for TypeScript by adding JSDoc comments:

/**
* Adds VAT to a price
*
* @param {number} price The price without VAT
* @param {number} vat The VAT [0-1]
*
* @returns {number}
*/

function addVAT(price, vat = 0.2) {
return price * (1 + vat)
}

Paul Lewis has a great video on that. But there’s a lot, lot more to it than a couple of basic types in comments. Turns out working with JSDoc type gets you very far.

Activating reports #

To make sure you not only provide type information, but get actual error feedback in your editor (or via tsc), please activate the @ts-check flag in your source files:

// @ts-check

If there’s one particular line that errors, but you think you know better, add the @ts-ignore flag:

// @ts-ignore
addVAT('1200', 0.1); // would error otherwise

Inline types #

Defining parameters is one thing. Sometimes you want to make sure that a variable, which hasn’t been assigned yet, has the correct type. TypeScript supports inline comment annotations.

/** @type {number} */
let amount;
amount = '12'; // 💥 does not work

Don’t forget the correct comment syntax. Inline comments with // won’t work.

Defining objects #

Basic types is one thing, but in JavaScript you usually deal with complex types and objects. No problem for comment based type annotations:

/**
* @param {[{ price: number, vat: number, title: string, sold?: boolean }]} articles
*/

function totalAmount(articles) {
return articles.reduce((total, article) => {
return total + addVAT(article)
}, 0)
}

See that we defined a complex object type (just like we would do in TypeScript) inline as a parameter.

Annotating everything inline can become crowded very quickly. There’s a more elegant way of defining object types through @typedef:

/**
* @typedef {Object} Article
* @property {number} price
* @property {number} vat
* @property {string} string
* @property {boolean=} sold
*/

/**
* Now we can use Article as a proper type
* @param {[Article]} articles
*/

function totalAmount(articles) {
return articles.reduce((total, article) => {
return total + addVAT(article)
}, 0)
}

More work writing, but ultimately more readable. Also TypeScript now can identify Article with the name Article, providing better information in your IDE.

Please note the optional parameter sold. It’s defined with @property {boolean=} sold. An alternative syntax is @property {boolean} [sold]. Same goes for function @params.

Defining functions #

Functions can be defined inline, just like their object counterparts:

/**
* @param {string} url
* @param {(status: number, response?: string) => void} cb
*/

function loadData(url, cb) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url)
xhr.onload = () => {
cb(xhr.status, xhr.responseText)
}
}

Again, this can get very confusing quickly. There’s the @callback annotation that helps with that:

/**
* @callback LoadingCallback
* @param {number} status
* @param {string=} response
* @returns {void}
*/

/**
* @param {string} url
* @param {LoadingCallback} cb
*/

function loadData(url, cb) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url)
xhr.onload = () => {
cb(xhr.status, xhr.responseText)
}
}

@callback takes the same parameters as function annotation, but works like @typedef

Importing types #

@typedef allows you to import types from any other .js or .ts file. With that you can write TypeScript type definitions in TypeScript and import them in your source files.

See article.ts:

export type Article = {
title: string,
price: number,
vat: number,
sold?: boolean,
}

And our main.js:

// The following line imports the Article type from article.ts and makes it
// available under Article
/** @typedef { import('./article').Article } Article */

/** @type {Article} */
const article = {
title: 'The best book in the world',
price: 10,
vat: 0.2
}

You can also import a type directly in the type annotation:

/** @type {import('./article').Article} */
const article = {
title: 'The best book in the world',
price: 10,
vat: 0.2
}

Great when working a mix of TypeScript where you don’t have ambient type definitions.

Working with generics #

TypeScript’s generics syntax is available wherever there’s a type that can be generic:

/** @type PromiseLike<string> */
let promise;

// checks. `then` is available, and x is a string
promise.then(x => x.toUpperCase())

But you can define more elaborate generics (esp. functions with generics) with the @template annotation:

/**
* @template T
* @param {T} obj
* @param {(keyof T)[]} params
*/

function pluck(obj, ...params) {
return params.map(el => obj[el])
}

Convenient, but a bit hard to do for complex generics. Inline generics still work the TypeScript way:

/** @type { <T, K extends keyof T>(obj: T, params: K[]) => Array<T[K]>} */
function values(obj, ...params) {
return params.map(el => obj[el])
}

const numbers = values(article, 'price', 'vat')
const strings = values(article, 'title')
const mixed = values(article, 'title', 'vat')

Have even more complex generics? Consider putting them in a TypeScript file and import it via the import function.

Enums #

Turn a specially structured JavaScript object into an enum and make sure values are consistent:

/** @enum {number} */
const HTTPStatusCodes = {
ok: 200,
forbidden: 403,
notFound: 404,
}

Enums differ greatly from regular TypeScript enums. They make sure that every key in this object has the specified type.

/** @enum {number} */
const HTTPStatusCodes = {
ok: 200,
forbidden: 403,
notFound: 404,
errorsWhenChecked: 'me' // 💣
}

That’s all they do.

typeof #

One of my most favourite tools, typeof is also available. Saves you a ton of editing:

/**
* @param {number} status The status code as a number
* @param {string} data The data to work with
*/

function defaultCallback(status, data) {
if(status === 200) {
document.body.innerHTML = data
}
}

/**
* @param {string} url the URL to load data from
* @param {typeof defaultCallback} cb what to do afterwards
*/

function loadData(url, cb) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url)
xhr.onload = () => {
cb(xhr.status, xhr.responseText)
}
}

extending and augmenting from classes #

The extends annotation allow you to specify generic parameters when extending from a basic JavaScript class. See the example below:

/**
* @template T
* @extends {Set<T>}
*/

class SortableSet extends Set {
// ...
}

@augments on the other hand allows you to be a lot more specific with generic parameters:

/**
* @augments {Set<string>}
*/

class StringSet extends Set {
// ...
}

Handy!

Bottom line #

TypeScript annotations in plain JavaScript go really far. There’s a little more to TypeScript especially when entering generics, but for a lot of basic tasks you get a lot of editor superpowers without installing any compiler at all.

Know more? Shoot me a tweet. I’m more than happy to add them here.

Related Articles