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

Use return type as an inference location #11152

Closed
mhegazy opened this issue Sep 26, 2016 · 18 comments
Closed

Use return type as an inference location #11152

mhegazy opened this issue Sep 26, 2016 · 18 comments
Assignees
Labels
Fixed A PR has been merged for this issue Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript

Comments

@mhegazy
Copy link
Contributor

mhegazy commented Sep 26, 2016

From #11054

interface FolderContentItem{
    type: 'folder' | 'file';
}

let a:FolderContentItem[] = [];
a = [1,2,3,4,5].map(v=>({type:'folder'}))

Today this is an error without casting "folder" to the literal type. We have a contextual type coming from a that is not being used.

@HerringtonDarkholme
Copy link
Contributor

Does this effectively mean curried function will be inferred?

const curry = <K, T>(k: K) => (t: T) => {}

curry(123)('123') // K inferred to number, T inferred to string

@hmaurer
Copy link

hmaurer commented Dec 14, 2016

That would be amazing to see. I stumbled upon a similar issue tonight, which is roughly the same as #1212. What is the progress on this?

@ghost
Copy link

ghost commented Jun 8, 2017

Another case where this would be useful:

function id<T>(x: T): T { return x; }
function f(b: boolean): { n: number } {
    if (b) {
        return { n: 1 };
    } else {
        return id({ n: 1 });
    }
}

Find-all-references on the declaration of n: number should find all 3 references to n, but today misses the one inside id().

@k8w
Copy link

k8w commented Nov 2, 2017

Still have this problem, and also see this:

let a = [[1, 2], [3, 4]];
let b: [number, number][] = a.map(v => v);
let c: [number, number][] = a.map(v => [1,2]);

Both b and c has compile error:

Type 'number[][]' is not assignable to type '[number, number][]'.

[1,2] is treated as number[] rather than [number, number]

@Pajn
Copy link

Pajn commented Jan 22, 2018

I'm moving the example from #21275 to here as I filed a duplicate

type Path<T, V> = Array<string>

function path<T, A extends keyof T>(key: A): Path<T, T[A]>
function path<T>(path: string|Array<string>): Path<T, any> {
  if (typeof path === 'string') return [path] as Path<T, any>
  else return path as Path<T, any>
}

function field<T, V>(path: Path<T, V>) {
  return {path}
}

type User = {name: string}

// Errors
field<User, string>(path('name'))

// Works
field<User, string>(path<User, 'name'>('name'))

Can I do anything to help get this feature going?

@DanielRosenwasser
Copy link
Member

Keywords: map on array of tuple contextual contextually typed return type of lambda arrow function expressions

@RyanCavanaugh
Copy link
Member

I think the only reason this doesn't work is because we widen the return type of the function expression?

@weswigham
Copy link
Member

weswigham commented Aug 1, 2018

Actually, I think #25937 maybe fixes (some of) this. Although undoubtedly @RyanCavanaugh is probably right - most of the remarks here are caused by the return type widening.

@ahejlsberg
Copy link
Member

Another example, simplified from #26621:

type Box<T> = { value: T };
declare function box<T>(value: T): Box<T>;

type WinCondition =
    | { type: 'win', player: string }
    | { type: 'draw' };

let zz: Box<WinCondition> = box({ type: 'draw' });  // Error

type WinType = 'win' | 'draw';

let yy: Box<WinType> = box('draw');  // Error

Would be nice if we could do better here.

@cshaa
Copy link

cshaa commented Sep 8, 2018

Seems like my proposal at #26979 could be a fix for this. While I don't propose the exact mechanism for literal type inference, I suggest an expression for type assertion that would prevent the type from widening. For example @k8w's code could be written this way:

const a = [[1, 2], [3, 4]] as const;
const b: Array<[number, number]> = a.map(v => v);
const c: Array<[number, number]> = a.map(v => [1,2] as const);

While I agree that implementing the interference mechanism is important, this could provide a quick fix.

@CyrusNajmabadi
Copy link
Contributor

Would definitely like to see this done. We've hit this over in Pulumi as part of #11312. It seems really unfortunate that something as simple as: new Map(arr.map(a => [a.foo, a.bar])) can't work properly.

@Jessidhia
Copy link

Looks like I hit this on DefinitelyTyped/DefinitelyTyped#30057 (comment)

@weswigham
Copy link
Member

I had a PR up that made return widening contextual, rather than always - didn't really get a great chance to review and iterate it before it got out of sync though. #20976 for reference.

@ahejlsberg
Copy link
Member

Fixed in #29478.

@CyrusNajmabadi
Copy link
Contributor

Thanks much @ahejlsberg ! This will be very helpful in many of our complex, highly generic code spots!

@jeremychone
Copy link

jeremychone commented Apr 2, 2019

I must be missing something. I installed Typescript 3.4.1, and still get the wrong inferrence.

interface Ent { id: number, name: string };

const entities: Ent[] = [{ id: 1, name: 'one' }, { id: 2, name: 'two' }];

// Wrong type inferred: (number | string)[][]
const wrongInferredType = entities.map(ent => [ent.id, ent.name]);

// This works: [number, string][] (as it should be)
const idEntities = entities.map(ent => [ent.id, ent.name]) as [number, string][];

@weswigham
Copy link
Member

@jeremychone nothing changed w.r.t where we infer tuple types (excepting const contexts) - you'd need to have something with a tuple type on the LHS of that assignment for it to be interpreted as a tuple.

@jeremychone
Copy link

@weswigham Thank you. In fact, just realized I did not understand the root of the problem (which was that without typing, returning an array could not be assumed to be a tuple).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Fixed A PR has been merged for this issue Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests