question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Easier destructuring with type annotations on binding patterns

See original GitHub issue

Search Terms

type inference destructuring syntax conflict

Suggestion

It’s currently not possible to destructure a value in Typescript and provide types for each of the values due to the syntax clash with destructuring and renaming at the same time. You can see exactly this issue in the Typescript FAQs at: https://github.com/Microsoft/TypeScript/wiki/FAQ#why-cant-i-use-x-in-the-destructuring-function-f-x-number------

This is frustrating when programming in React where it’s very common to see this pattern:

const MyComponent = ({ a, b }) => {
    // ...
}

But in Typescript a and b are untyped (inferred to have type any) and type annotation must be added (either to aid in type safety or to avoid compiler errors, depending on the state of the user’s strict flags). To add the correct type annotation it feels natural to write:

const MyComponent = ({ a : string, b : number }) => {
    // ...
}

but that’s not what the user thinks due to the aforementioned syntax clash. The only valid syntax in Typescript is actually this:

const MyComponent = ({ a, b } : { a : string, b : number }) => {
    // ...
}

Which is very strange to write and difficult to read when the object has more than two parameters or the parameters have longer names. Also the value names have been duplicated – once in the destructuring and once in the type annotation.

I suggest we allow some other symbol (my current thinking is a double colon) to make the syntax unambiguous in this specific scenario:

const MyComponent = ({ a :: string, b :: number }) => {
	// ...
}

Although this is really the only place it would be used, for the sake of consistency, I think is should be allowed everywhere:

const a :: string = "";
const b :: number = 1;

Use Cases

It would allow for type-safe destructuring of values where the type cannot be inferred by the compiler (such as function parameters).

Examples

A good example of the sort of React components I’m talking about (and one of the first Google results for React Functional Components) can be found at https://hackernoon.com/react-stateless-functional-components-nine-wins-you-might-have-overlooked-997b0d933dbc. We can use my proposed syntax in the functional component definition:

import React from 'react'

const HelloWorld = ({name :: string}) => {
	const sayHi = (event) => {
		alert(`Hi ${name}`)
	}

	return (
		<div>
			<a href="#"
			   onclick={sayHi}>Say Hi</a>
		</div>
	)
}

Checklist

My suggestion meets these guidelines:

  • This wouldn’t be a breaking change in existing TypeScript/JavaScript code
  • This wouldn’t change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn’t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript’s Design Goals.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:177
  • Comments:71 (9 by maintainers)

github_iconTop GitHub Comments

62reactions
RyanCavanaughcommented, Jan 23, 2019

I agree it’s a duplicate but it really does suck. We should try again.

60reactions
dragomirtitiancommented, Jul 31, 2019

Actually the more I think about :: the more I like it 😁, but with a slightly different meaning. Let’s not consider :: as a new way to introduce a type annotation, but rather an empty rename.

// rename, no type annotations
const HelloWorld = ({name : newName }) => { 

}
// rename, with annotation 
const HelloWorld = ({name : newName : string }) => { 

}

// empty rename, with annotation 
const HelloWorld = ({name :: string }) => { 

}
// empty rename, with annotation, and default
const HelloWorld = ({name :: string = "default" }) => { 

}

I personally think it flows nicely. The first : always introduces a name binding, which can be empty, the second : always introduces a type annotation, and since a new : not valid here in ES it would not be ambiguous.

Would work decently with objects too:

type FooBar = { foo: string; bar: number }
const HelloWorld = ({ a, b:{ foo: fooLocal, bar: barLocal }: FooBar  }) => {
  
}

const HelloWorld = ({ a, b::FooBar  }) => {
  
}

const HelloWorld = ({ 
  a: number, 
  b: { 
    foo:fooLocal:string, 
    bar:barLocal:string
  }
}) => {
  
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

typescript - Types in object destructuring - Stack Overflow
It is what is usually called a "deconstructing assignment pattern". What you are seeing here is actually a special TypeScript feature which allows...
Read more >
Typing Destructured Object Parameters in TypeScript
Here's the TypeScript syntax for typing the destructured properties. ... In TypeScript, you can add a type annotation to each formal ...
Read more >
Destructuring binds · Unison programming language
Tuples and data types can be decomposed withpattern matching,but Unison also provides several ways to more concisely bind variable names to the corresponding ......
Read more >
[Pitch] Destructuring Assignment of Structs and Classes
0 and car.1 which can be easy to mix up. You can see how destructuring syntax makes this pattern more ergonomic. While this...
Read more >
Announcing TypeScript 4.8 RC - Microsoft Developer Blogs
In some cases, TypeScript will pick up a type from a binding pattern to make better inferences. declare function chooseRandomly<T>(x: T, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found