Repository navigation
Destructuring array result of function with generics call sometimes lead to type loss #32003
Description
Activity
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). Sincefoois 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 inferanyforT, 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 forTat all and therefore end up with the default{}.Ideally we shouldn't make
anyinferences 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.- addedDesign LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed
on Jun 21, 2019 miloszpp commented
on Aug 22, 2019 ContributorMore actionsJust wanted to point out that this issue impacts typing hooks in React.
const [pill, setPill] = useState();
The type of
pillis inferred toanywhile it should beundefinedaccording to the type definition:function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
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
Reacted by Nicholas Firth-McCoyexport interface Sizes { width: number | null; height: number | null; } declare function useResizeAware<S = Sizes>( customReporter?: (target: HTMLElement | null) => S ): [string, S]; const [a, b] = useResizeAware(); const c = useResizeAware(); const d = c[1]So in my case the generic does appear in the arguments but it is an optional parameter.
dis properly typed asSizeswhilebis of typeany.Using overrides is another solution that fixed this for me:
declare function useResizeAware(): [string, Sizes]; declare function useResizeAware<S>( customReporter: (target: HTMLElement | null) => S ): [string, S];Looks like TypeScript v3.8 have fixed that issue?
Reacted by Mathieu SavyIn can confirm that issue is fixed in both TS 3.8 and 3.9. Thanks!
Reacted by Piotr Kowalski, Gaurav and Quan Tran
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
anyinstead of the generic type. The problem doesn't show if the generic is explicitly provided. Snippets 1, 3 and 4 works as expected.Code
Expected behavior:
xtype is{}Actual behavior:
xtype isanyPlayground 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 :)