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

推荐订阅源

T
Tenable Blog
博客园_首页
Vercel News
Vercel News
WordPress大学
WordPress大学
美团技术团队
G
Google Developers Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Y
Y Combinator Blog
博客园 - 【当耐特】
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
T
The Blog of Author Tim Ferriss
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
云风的 BLOG
云风的 BLOG
腾讯CDC
M
MIT News - Artificial intelligence
爱范儿
爱范儿
Recent Announcements
Recent Announcements
雷峰网
雷峰网
Last Week in AI
Last Week in AI
宝玉的分享
宝玉的分享
The Register - Security
The Register - Security
Jina AI
Jina AI
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
P
Privacy & Cybersecurity Law Blog
Recorded Future
Recorded Future
Help Net Security
Help Net Security
N
News and Events Feed by Topic
博客园 - Franky
P
Proofpoint News Feed
L
LINUX DO - 热门话题
S
SegmentFault 最新的问题
The GitHub Blog
The GitHub Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
D
Docker
Google DeepMind News
Google DeepMind News
有赞技术团队
有赞技术团队
IT之家
IT之家
Security Latest
Security Latest
L
LangChain Blog
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
J
Java Code Geeks

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: 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
TypeScript: Variadic Tuple Types Preview
2020-06-19 · via oida.dev | TypeScript, Rust

TypeScript 4.0 is supposed to be released in August 2020, and one of the biggest changes in this release will be variadic tuple types. And even though his feature is hot of the press at the time of this writing, it’s worth checking out and see what we can do with it. Note that stuff here might be subject to change, so be cautious! I will try to keep this page up to date until 4.0 is in RC or released.

If you want to try it out yourself, you can load an early version of the branch into the TypeScript playground.

Variadic tuples #

A tuple type in TypeScript is an array with the following features.

  1. The length of the array is defined.
  2. The type of each element is known (and does not have to be the same).

For example, this is a tuple type:

type PersonProps = [string, number]

const [name, age]: PersonProps = ['Stefan', 37]

A variadic tuple type is a tuple type that has the same properties — defined length and the type of each element is known — but where the exact shape is yet to be defined.

An example straight out of the pull request

type Foo<T extends unknown[]> = [string, ...T, number];

type T1 = Foo<[boolean]>; // [string, boolean, number]
type T2 = Foo<[number, number]>; // [string, number, number, number]
type T3 = Foo<[]>; // [string, number]

We had something similar already with rest elements in functions (more on that later), but the big difference is that variadic tuple types can happen anywhere in the tuple and multiple times.

type Bar<
T extends unknown[],
U extends unknown[]
>
= [...T, string, ...U];

type T4 = Bar<[boolean], [number]>; // [boolean, string, number]
type T5 = Bar<[number, number], [boolean]>; // [number, number, string, boolean]
type T6 = Bar<[], []>; // [string]

Cool already! But why do we care so much about it?

Function arguments are tuples #

Every function head can be described in a tuple type. For example:

declare function hello(name: string, msg: string): void;

Is the same as:

declare function hello(...args: [string, string]): void;

And we can be very flexible in defining it:

declare function h(a: string, b: string, c: string): void
// equal to
declare function h(a: string, b: string, ...r: [string]): void
// equal to
declare function h(a: string, ...r: [string, string]): void
// equal to
declare function h(...r: [string, string, string]): void

This is also known as rest elements, something that we have in JavaScript and that allows you to define functions with an almost limitless argument list, where the last element, the rest element sucks all excess arguments in.

We can use this, e.g. for this generic tuple function takes an argument list of any type and creates a tuple out of it:

function tuple<T extends any[]>(...args: T): T {
return args;
}

const numbers: number[] = getArrayOfNumbers();
const t1 = tuple("foo", 1, true); // [string, number, boolean]
const t2 = tuple("bar", ...numbers); // [string, ...number[]]

The thing is, rest elements always have to be last. In JavaScript, it’s not possible to define an almost endless argument list just somewhere in between.

With variadic tuple types however, we can do this! For example, this is a function type where the argument list at the beginning is not defined, but the last element has to be a function:

type HasCallback<T extends unknown[]> =
(...t: [...T, (...args: any[]) => any]) => void;

declare const foo: HasCallback<[string]>

foo('hello', function() {}) // 👍
foo('hello') // 💥 breaks

declare const bar: HasCallback<[string, number]>

bar('hello', 2, function() {}) // 👍
bar('hello', function() {}) // 💥 breaks
bar('hello', 2) // 💥 breaks

This is now with an explicit type annotation, but as always with generics, we can also infer them by usage 😎 Which brings me to a solution for an interesting problem.

Typing promisify #

Functions that take a callback at the end are common in async programming. In Node.js you encounter this pattern all the time. The argument list before the callback varies based on the purpose of the function.

Here are a couple of fictional examples:

// loads a file, you can set the encoding
// the callback gets the contents of the file
declare function load(
file: string,
encoding: string,
callback: (result: string) => void): void

// Calls a user defined function based on
// an event. The event can be one of 4 messages
type Messages = 'open' | 'write' | 'end' | 'error'
declare function on(
msg: Messages,
callback: (msg: { type: Messages, content: string}) => void
): void

When you program async, you might want to use promises. There is a nice function to promisify callback-based functions. They take the same argument list as the callback-based function, but instead of taking a callback, they return a Promise with the result.

We can type this using variadic tuple types.

First, we design a type that infers all arguments except for the last one.

type InferArguments<T> =
T extends (... t: [...infer Arg, (...args: any) => any]) => any ?
Arg : never

It reads that T is a function that has rest elements where the tuple consists of

  • Any variadic tuple Arg that we infer
  • A callback function with any arguments

We return Arg.

We also want to infer the result from the callback function. Similar type, slightly modified:

type InferCallbackResults<T> = 
T extends (... t: [...infer Arg, (res: infer Res) => any]) => any ?
Res : never

The promisify function takes any function that matches the shape of arguments + callback. It returns a function that has the same argument list except for the callback. This function then returns a promise with the results of the callback. 😅

declare function promisify<
// Fun is the function we want to promisify
Fun extends (...arg: any[]) => any
>(f: Fun):
// we return a function with the same argument list
// except the callback
(...args: InferArguments<Fun>)
// this function in return returns a promise
// with the same results as the callback
=> Promise<InferCallbackResults<Fun>>

This declaration is already pretty fine, the implementation of the function body checks without type casts, which means that the types are really sound:

function promisify<
Fun extends (...args: any[]) => any
>(f: Fun): (...args: InferArguments<Fun>) => Promise<InferCallbackResults<Fun>> {
return function(...args: InferArguments<Fun>) {
return new Promise((resolve) => {
function callback(result: InferCallbackResults<Fun>) {
resolve(result)
}
args.push(callback);
f.call(null, ...args)
})
}
}

In action:

const loadPromise = promisify(load)

loadPromise('./text.md', 'utf-8').then(res => {
// res is string! 👍
})

const onPromise = promisify(on)

onPromise('open').then(res => {
console.log(res.content) // content and type infered 👍
})

The nicest part of all this is that we retain the argument names. When we call the loadPromise, we still know that the arguments are file and encoding. ❤️

Further reading #

Keep an eye on this blog! 😉

Related Articles