Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Destructuring array result of function with generics call sometimes lead to type loss #32003

Open
mrblackus opened this issue Jun 20, 2019 · 3 comments

Comments

@mrblackus
Copy link

commented Jun 20, 2019

Hi!

TypeScript Version: 3.5.2

Search Terms: destructuring result function generics type loss any

Problem is only with snippet 2, when I get the third parameter with destructuring, the first one became any instead of the generic type. The problem doesn't show if the generic is explicitly provided. Snippets 1, 3 and 4 works as expected.

Code

function foo<T = {}>(): [T, string, number] {
	let a: T;
	return [a, 'foo', 2];
}

// 1
const [a, b] = foo();
// a: {}, b: string

// 2
const [x, y, z] = foo();
// x: any, y: string, z: number

// 3
const res = foo();
const [x2, y2, z2] = res;
// x2: {}, y: string, z: number

// 4
const [x3, y3, z3] = foo<{ bar: string }>();
// x3: { bar: string }, y3: string, z: number

Expected behavior:
x type is {}

Actual behavior:
x type is any

Playground Link: https://www.typescriptlang.org/play/#src=%0D%0Afunction%20foo%3CT%20%3D%20%7B%7D%3E()%3A%20%5BT%2C%20string%2C%20number%5D%20%7B%0D%0A%09let%20a%3A%20T%3B%0D%0A%09return%20%5Ba%2C%20'foo'%2C%202%5D%3B%0D%0A%7D%0D%0A%0D%0Aconst%20%5Ba%2C%20b%5D%20%3D%20foo()%3B%0D%0A%2F%2F%20a%3A%20%7B%7D%2C%20b%3A%20string%0D%0A%0D%0Aconst%20%5Bx%2C%20y%2C%20z%5D%20%3D%20foo()%3B%0D%0A%2F%2F%20x%3A%20any%2C%20y%3A%20string%2C%20z%3A%20number%0D%0A%0D%0Aconst%20res%20%3D%20foo()%3B%0D%0Aconst%20%5Bx2%2C%20y2%2C%20z2%5D%20%3D%20res%3B%0D%0A%2F%2F%20x2%3A%20%7B%7D%2C%20y%3A%20string%2C%20z%3A%20number%0D%0A%0D%0Aconst%20%5Bx3%2C%20y3%2C%20z3%5D%20%3D%20foo%3C%7B%20bar%3A%20string%20%7D%3E()%3B%0D%0A%2F%2F%20x3%3A%20%7B%20bar%3A%20string%20%7D%2C%20y3%3A%20string%2C%20z%3A%20number

Related Issues:
Some issues are covering topics that seems related, but I couldn't find one with my specific issue, sorry if there's already a duplicate for this one!

Thanks :)

@ahejlsberg

This comment has been minimized.

Copy link
Member

commented Jun 21, 2019

This one is a bit strange. In examples 1 and 2, the call to foo() is contextually typed by the implied types of the array destrucuring patterns ([any, any] and [any, any, any] respectively). Since foo is generic and we have a contextual type, we perform generic return type inference (#16072). In example 2 where the arities of the tuple types match we then infer any for T, and since there is nothing else to make inferences from, that's what we end up with. In the other examples, we make no inferences for T at all and therefore end up with the default {}.

Ideally we shouldn't make any inferences from the implied types of the destructuring patterns, but in other scenarios we do want a contextual type of a tuple type to indicate that array literals should be treated as tuples. So we have conflicting goals and there isn't an obvious simple fix. Add to that the anti-pattern of having a type parameter that isn't used in anywhere in the parameter list (it's basically just a disguised type assertion) and I think we'll call this a design limitation.

@miloszpp

This comment has been minimized.

Copy link
Contributor

commented Aug 22, 2019

Just wanted to point out that this issue impacts typing hooks in React.

const [pill, setPill] = useState();

The type of pill is inferred to any while it should be undefined according to the type definition:

function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
@danieloprado

This comment has been minimized.

Copy link

commented Sep 10, 2019

A little workaround:

function foo<T = {}>(): [T, string, number, undefined] { // <~ add a 4th return
	let a: T;
	return [a, 'foo', 2, undefined]; // <~ return the 4th value as undefined
}

// 1
const [a, b] = foo();
// a: {}, b: string

// 2
const [x, y, z] = foo();
// x:{}, y: string, z: number 👍

// 3
const res = foo();
const [x2, y2, z2] = res;
// x2: {}, y: string, z: number

// 4
const [x3, y3, z3] = foo<{ bar: string }>();
// x3: { bar: string }, y3: string, z: number
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.