mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-29 16:29:19 -05:00
Merge pull request #16072 from Microsoft/improveTypeArgumentInference
Infer from generic function return types
This commit is contained in:
70
tests/cases/compiler/inferFromGenericFunctionReturnTypes1.ts
Normal file
70
tests/cases/compiler/inferFromGenericFunctionReturnTypes1.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
// Repro from #15680
|
||||
|
||||
// This is a contrived class. We could do the same thing with Observables, etc.
|
||||
class SetOf<A> {
|
||||
_store: A[];
|
||||
|
||||
add(a: A) {
|
||||
this._store.push(a);
|
||||
}
|
||||
|
||||
transform<B>(transformer: (a: SetOf<A>) => SetOf<B>): SetOf<B> {
|
||||
return transformer(this);
|
||||
}
|
||||
|
||||
forEach(fn: (a: A, index: number) => void) {
|
||||
this._store.forEach((a, i) => fn(a, i));
|
||||
}
|
||||
}
|
||||
|
||||
function compose<A, B, C, D, E>(
|
||||
fnA: (a: SetOf<A>) => SetOf<B>,
|
||||
fnB: (b: SetOf<B>) => SetOf<C>,
|
||||
fnC: (c: SetOf<C>) => SetOf<D>,
|
||||
fnD: (c: SetOf<D>) => SetOf<E>,
|
||||
):(x: SetOf<A>) => SetOf<E>;
|
||||
/* ... etc ... */
|
||||
function compose<T>(...fns: ((x: T) => T)[]): (x: T) => T {
|
||||
return (x: T) => fns.reduce((prev, fn) => fn(prev), x);
|
||||
}
|
||||
|
||||
function map<A, B>(fn: (a: A) => B): (s: SetOf<A>) => SetOf<B> {
|
||||
return (a: SetOf<A>) => {
|
||||
const b: SetOf<B> = new SetOf();
|
||||
a.forEach(x => b.add(fn(x)));
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
function filter<A>(predicate: (a: A) => boolean): (s: SetOf<A>) => SetOf<A> {
|
||||
return (a: SetOf<A>) => {
|
||||
const result = new SetOf<A>();
|
||||
a.forEach(x => {
|
||||
if (predicate(x)) result.add(x);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const testSet = new SetOf<number>();
|
||||
testSet.add(1);
|
||||
testSet.add(2);
|
||||
testSet.add(3);
|
||||
|
||||
testSet.transform(
|
||||
compose(
|
||||
filter(x => x % 1 === 0),
|
||||
map(x => x + x),
|
||||
map(x => x + '!!!'),
|
||||
map(x => x.toUpperCase())
|
||||
)
|
||||
)
|
||||
|
||||
testSet.transform(
|
||||
compose(
|
||||
filter(x => x % 1 === 0),
|
||||
map(x => x + x),
|
||||
map(x => 123), // Whoops a bug
|
||||
map(x => x.toUpperCase()) // causes an error!
|
||||
)
|
||||
)
|
||||
94
tests/cases/compiler/inferFromGenericFunctionReturnTypes2.ts
Normal file
94
tests/cases/compiler/inferFromGenericFunctionReturnTypes2.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
type Mapper<T, U> = (x: T) => U;
|
||||
|
||||
declare function wrap<T, U>(cb: Mapper<T, U>): Mapper<T, U>;
|
||||
|
||||
declare function arrayize<T, U>(cb: Mapper<T, U>): Mapper<T, U[]>;
|
||||
|
||||
declare function combine<A, B, C>(f: (x: A) => B, g: (x: B) => C): (x: A) => C;
|
||||
|
||||
declare function foo(f: Mapper<string, number>): void;
|
||||
|
||||
let f1: Mapper<string, number> = s => s.length;
|
||||
let f2: Mapper<string, number> = wrap(s => s.length);
|
||||
let f3: Mapper<string, number[]> = arrayize(wrap(s => s.length));
|
||||
let f4: Mapper<string, boolean> = combine(wrap(s => s.length), wrap(n => n >= 10));
|
||||
|
||||
foo(wrap(s => s.length));
|
||||
|
||||
let a1 = ["a", "b"].map(s => s.length);
|
||||
let a2 = ["a", "b"].map(wrap(s => s.length));
|
||||
let a3 = ["a", "b"].map(wrap(arrayize(s => s.length)));
|
||||
let a4 = ["a", "b"].map(combine(wrap(s => s.length), wrap(n => n > 10)));
|
||||
let a5 = ["a", "b"].map(combine(identity, wrap(s => s.length)));
|
||||
let a6 = ["a", "b"].map(combine(wrap(s => s.length), identity));
|
||||
|
||||
// This is a contrived class. We could do the same thing with Observables, etc.
|
||||
class SetOf<A> {
|
||||
_store: A[];
|
||||
|
||||
add(a: A) {
|
||||
this._store.push(a);
|
||||
}
|
||||
|
||||
transform<B>(transformer: (a: SetOf<A>) => SetOf<B>): SetOf<B> {
|
||||
return transformer(this);
|
||||
}
|
||||
|
||||
forEach(fn: (a: A, index: number) => void) {
|
||||
this._store.forEach((a, i) => fn(a, i));
|
||||
}
|
||||
}
|
||||
|
||||
function compose<A, B, C, D, E>(
|
||||
fnA: (a: SetOf<A>) => SetOf<B>,
|
||||
fnB: (b: SetOf<B>) => SetOf<C>,
|
||||
fnC: (c: SetOf<C>) => SetOf<D>,
|
||||
fnD: (c: SetOf<D>) => SetOf<E>,
|
||||
):(x: SetOf<A>) => SetOf<E>;
|
||||
/* ... etc ... */
|
||||
function compose<T>(...fns: ((x: T) => T)[]): (x: T) => T {
|
||||
return (x: T) => fns.reduce((prev, fn) => fn(prev), x);
|
||||
}
|
||||
|
||||
function map<A, B>(fn: (a: A) => B): (s: SetOf<A>) => SetOf<B> {
|
||||
return (a: SetOf<A>) => {
|
||||
const b: SetOf<B> = new SetOf();
|
||||
a.forEach(x => b.add(fn(x)));
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
function filter<A>(predicate: (a: A) => boolean): (s: SetOf<A>) => SetOf<A> {
|
||||
return (a: SetOf<A>) => {
|
||||
const result = new SetOf<A>();
|
||||
a.forEach(x => {
|
||||
if (predicate(x)) result.add(x);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const testSet = new SetOf<number>();
|
||||
testSet.add(1);
|
||||
testSet.add(2);
|
||||
testSet.add(3);
|
||||
|
||||
const t1 = testSet.transform(
|
||||
compose(
|
||||
filter(x => x % 1 === 0),
|
||||
map(x => x + x),
|
||||
map(x => x + '!!!'),
|
||||
map(x => x.toUpperCase())
|
||||
)
|
||||
)
|
||||
|
||||
declare function identity<T>(x: T): T;
|
||||
|
||||
const t2 = testSet.transform(
|
||||
compose(
|
||||
filter(x => x % 1 === 0),
|
||||
identity,
|
||||
map(x => x + '!!!'),
|
||||
map(x => x.toUpperCase())
|
||||
)
|
||||
)
|
||||
@@ -1,48 +1,48 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////interface Collection<T, U> {
|
||||
//// length: number;
|
||||
//// add(x: T, y: U): void ;
|
||||
//// remove(x: T, y: U): boolean;
|
||||
////}
|
||||
//// length: number;
|
||||
//// add(x: T, y: U): void ;
|
||||
//// remove(x: T, y: U): boolean;
|
||||
////}
|
||||
////
|
||||
////interface Combinators {
|
||||
//// map<T, U, V>(c: Collection<T, U>, f: (x: T, y: U) => V): Collection<T, V>;
|
||||
//// map<T, U>(c: Collection<T, U>, f: (x: T, y: U) => any): Collection<any, any>;
|
||||
////}
|
||||
//// map<T, U, V>(c: Collection<T, U>, f: (x: T, y: U) => V): Collection<T, V>;
|
||||
//// map<T, U>(c: Collection<T, U>, f: (x: T, y: U) => any): Collection<any, any>;
|
||||
////}
|
||||
////
|
||||
////class A {
|
||||
//// foo<T>(): T { return null; }
|
||||
////}
|
||||
//// foo<T>(): T { return null; }
|
||||
////}
|
||||
////
|
||||
////class B<T> {
|
||||
//// foo(x: T): T { return null; }
|
||||
////}
|
||||
//// foo(x: T): T { return null; }
|
||||
////}
|
||||
////
|
||||
////var c1: Collection<any, any>;
|
||||
////var c2: Collection<number, string>;
|
||||
////var c3: Collection<Collection<number, number>, string>;
|
||||
////var c4: Collection<number, A>;
|
||||
////var c5: Collection<number, B<any>>;
|
||||
////}
|
||||
////
|
||||
////var _: Combinators;
|
||||
////// param help on open paren for arg 2 should show 'number' not T or 'any'
|
||||
////// x should be contextually typed to number
|
||||
////var rf1 = (x: number, y: string) => { return x.toFixed() };
|
||||
////var rf2 = (x: Collection<number, number>, y: string) => { return x.length };
|
||||
////var rf3 = (x: number, y: A) => { return y.foo() };
|
||||
////}
|
||||
////
|
||||
////var /*9*/r1a = _.map/*1c*/(c2, (/*1a*/x, /*1b*/y) => { return x.toFixed() });
|
||||
////var /*10*/r1b = _.map(c2, rf1);
|
||||
////}
|
||||
////
|
||||
////var /*11*/r2a = _.map(c3, (/*2a*/x, /*2b*/y) => { return x.length });
|
||||
////var /*12*/r2b = _.map(c3, rf2);
|
||||
////}
|
||||
////
|
||||
////var /*13*/r3a = _.map(c4, (/*3a*/x, /*3b*/y) => { return y.foo() });
|
||||
////var /*14*/r3b = _.map(c4, rf3);
|
||||
////}
|
||||
////
|
||||
////var /*15*/r4a = _.map(c5, (/*4a*/x, /*4b*/y) => { return y.foo() });
|
||||
////}
|
||||
////
|
||||
////var /*17*/r5a = _.map<number, string, Date>(c2, /*17error1*/(/*5a*/x, /*5b*/y) => { return x.toFixed() }/*17error2*/);
|
||||
////var rf1b = (x: number, y: string) => { return new Date() };
|
||||
////var /*18*/r5b = _.map<number, string, Date>(c2, rf1b);
|
||||
@@ -51,7 +51,7 @@
|
||||
////var rf2b = (x: Collection<number, number>, y: string) => { return new Date(); };
|
||||
////var /*20*/r6b = _.map<Collection<number, number>, string, Date>(c3, rf2b);
|
||||
////
|
||||
////var /*21*/r7a = _.map<number, A, string>(c4, /*21error1*/(/*7a*/x,/*7b*/y) => { return y.foo() }/*21error2*/);
|
||||
////var /*21*/r7a = _.map<number, A, string>(c4, (/*7a*/x,/*7b*/y) => { return y.foo() });
|
||||
////var /*22*/r7b = _.map<number, A, string>(c4, /*22error1*/rf3/*22error2*/);
|
||||
////
|
||||
////var /*23*/r8a = _.map<number, /*error1*/B/*error2*/, string>(c5, (/*8a*/x,/*8b*/y) => { return y.foo() });
|
||||
@@ -89,5 +89,4 @@ verify.quickInfos({
|
||||
|
||||
verify.errorExistsBetweenMarkers('error1', 'error2');
|
||||
verify.errorExistsBetweenMarkers('17error1', '17error2');
|
||||
verify.errorExistsBetweenMarkers('21error1', '21error2');
|
||||
verify.errorExistsBetweenMarkers('22error1', '22error2');
|
||||
Reference in New Issue
Block a user