From https://github.com/Microsoft/TypeScript/issues/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.
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
That would be amazing to see. I stumbled upon a similar issue tonight, which is roughly the same as https://github.com/Microsoft/TypeScript/issues/1212. What is the progress on this?
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()
.
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]
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?
Keywords: map on array of tuple contextual contextually typed return type of lambda arrow function expressions
I think the only reason this doesn't work is because we widen the return type of the function expression?
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.
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.
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.
Would definitely like to see this done. We've hit this over in Pulumi as part of https://github.com/Microsoft/TypeScript/issues/11312. It seems really unfortunate that something as simple as: new Map(arr.map(a => [a.foo, a.bar]))
can't work properly.
Looks like I hit this on https://github.com/DefinitelyTyped/DefinitelyTyped/pull/30057#discussion_r232123748
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.
Fixed in #29478.
Thanks much @ahejlsberg ! This will be very helpful in many of our complex, highly generic code spots!
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][];
@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.
@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).
Most helpful comment
Still have this problem, and also see this:
Both
b
andc
has compile error:[1,2] is treated as number[] rather than [number, number]