mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Make getContextualTypeOfApparentType mapType over unions (#17668)
* Instantiate contextual types while in an inferrential context * Limit scope of instantiation to only when likely needed * Still get aparent type * Expand test * Fix nit * Handle JSX and array * Tests for the JSX and Array cases * After much deliberation and inspection, much simpler fix After much deliberation and inspection, much simpler fix Undo Redo
This commit is contained in:
parent
4f48bf80fe
commit
0593ba27d8
@ -13691,7 +13691,7 @@ namespace ts {
|
||||
// be "pushed" onto a node using the contextualType property.
|
||||
function getApparentTypeOfContextualType(node: Expression): Type {
|
||||
const type = getContextualType(node);
|
||||
return type && getApparentType(type);
|
||||
return type && mapType(type, getApparentType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
tests/cases/compiler/index.tsx(73,34): error TS7006: Parameter 's' implicitly has an 'any' type.
|
||||
|
||||
|
||||
==== tests/cases/compiler/index.tsx (1 errors) ====
|
||||
interface ActionsObject<State> {
|
||||
[prop: string]: (state: State) => State;
|
||||
}
|
||||
|
||||
interface Options<State, Actions> {
|
||||
state?: State;
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
actions: string | Actions;
|
||||
}
|
||||
|
||||
declare function app<State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>): void;
|
||||
|
||||
app({
|
||||
state: 100,
|
||||
actions: {
|
||||
foo: s => s // Should be typed number => number
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
});
|
||||
|
||||
|
||||
interface Bar {
|
||||
bar: (a: number) => void;
|
||||
}
|
||||
|
||||
declare function foo<T extends Bar>(x: string | T): T;
|
||||
|
||||
const y = foo({
|
||||
bar(x) { // Should be typed number => void
|
||||
}
|
||||
});
|
||||
|
||||
interface Options2<State, Actions> {
|
||||
state?: State;
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
actions?: Actions;
|
||||
}
|
||||
|
||||
declare function app2<State, Actions extends ActionsObject<State>>(obj: Options2<State, Actions>): void;
|
||||
|
||||
app2({
|
||||
state: 100,
|
||||
actions: {
|
||||
foo: s => s // Should be typed number => number
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
});
|
||||
|
||||
|
||||
type ActionsArray<State> = ((state: State) => State)[];
|
||||
|
||||
declare function app3<State, Actions extends ActionsArray<State>>(obj: Options<State, Actions>): void;
|
||||
|
||||
app3({
|
||||
state: 100,
|
||||
actions: [
|
||||
s => s // Should be typed number => number
|
||||
],
|
||||
view: (s, a) => undefined as any,
|
||||
});
|
||||
|
||||
namespace JSX {
|
||||
export interface Element {}
|
||||
export interface IntrinsicElements {}
|
||||
}
|
||||
|
||||
interface ActionsObjectOr<State> {
|
||||
[prop: string]: ((state: State) => State) | State;
|
||||
}
|
||||
|
||||
declare function App4<State, Actions extends ActionsObjectOr<State>>(props: Options<State, Actions>["actions"] & { state: State }): JSX.Element;
|
||||
|
||||
const a = <App4 state={100} foo={s => s} />; // TODO: should be number => number, but JSX resolution is missing an inferential pass
|
||||
~
|
||||
!!! error TS7006: Parameter 's' implicitly has an 'any' type.
|
||||
|
||||
103
tests/baselines/reference/contextualTypingOfOptionalMembers.js
Normal file
103
tests/baselines/reference/contextualTypingOfOptionalMembers.js
Normal file
@ -0,0 +1,103 @@
|
||||
//// [index.tsx]
|
||||
interface ActionsObject<State> {
|
||||
[prop: string]: (state: State) => State;
|
||||
}
|
||||
|
||||
interface Options<State, Actions> {
|
||||
state?: State;
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
actions: string | Actions;
|
||||
}
|
||||
|
||||
declare function app<State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>): void;
|
||||
|
||||
app({
|
||||
state: 100,
|
||||
actions: {
|
||||
foo: s => s // Should be typed number => number
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
});
|
||||
|
||||
|
||||
interface Bar {
|
||||
bar: (a: number) => void;
|
||||
}
|
||||
|
||||
declare function foo<T extends Bar>(x: string | T): T;
|
||||
|
||||
const y = foo({
|
||||
bar(x) { // Should be typed number => void
|
||||
}
|
||||
});
|
||||
|
||||
interface Options2<State, Actions> {
|
||||
state?: State;
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
actions?: Actions;
|
||||
}
|
||||
|
||||
declare function app2<State, Actions extends ActionsObject<State>>(obj: Options2<State, Actions>): void;
|
||||
|
||||
app2({
|
||||
state: 100,
|
||||
actions: {
|
||||
foo: s => s // Should be typed number => number
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
});
|
||||
|
||||
|
||||
type ActionsArray<State> = ((state: State) => State)[];
|
||||
|
||||
declare function app3<State, Actions extends ActionsArray<State>>(obj: Options<State, Actions>): void;
|
||||
|
||||
app3({
|
||||
state: 100,
|
||||
actions: [
|
||||
s => s // Should be typed number => number
|
||||
],
|
||||
view: (s, a) => undefined as any,
|
||||
});
|
||||
|
||||
namespace JSX {
|
||||
export interface Element {}
|
||||
export interface IntrinsicElements {}
|
||||
}
|
||||
|
||||
interface ActionsObjectOr<State> {
|
||||
[prop: string]: ((state: State) => State) | State;
|
||||
}
|
||||
|
||||
declare function App4<State, Actions extends ActionsObjectOr<State>>(props: Options<State, Actions>["actions"] & { state: State }): JSX.Element;
|
||||
|
||||
const a = <App4 state={100} foo={s => s} />; // TODO: should be number => number, but JSX resolution is missing an inferential pass
|
||||
|
||||
|
||||
//// [index.jsx]
|
||||
app({
|
||||
state: 100,
|
||||
actions: {
|
||||
foo: function (s) { return s; } // Should be typed number => number
|
||||
},
|
||||
view: function (s, a) { return undefined; }
|
||||
});
|
||||
var y = foo({
|
||||
bar: function (x) {
|
||||
}
|
||||
});
|
||||
app2({
|
||||
state: 100,
|
||||
actions: {
|
||||
foo: function (s) { return s; } // Should be typed number => number
|
||||
},
|
||||
view: function (s, a) { return undefined; }
|
||||
});
|
||||
app3({
|
||||
state: 100,
|
||||
actions: [
|
||||
function (s) { return s; } // Should be typed number => number
|
||||
],
|
||||
view: function (s, a) { return undefined; }
|
||||
});
|
||||
var a = <App4 state={100} foo={function (s) { return s; }}/>; // TODO: should be number => number, but JSX resolution is missing an inferential pass
|
||||
@ -0,0 +1,235 @@
|
||||
=== tests/cases/compiler/index.tsx ===
|
||||
interface ActionsObject<State> {
|
||||
>ActionsObject : Symbol(ActionsObject, Decl(index.tsx, 0, 0))
|
||||
>State : Symbol(State, Decl(index.tsx, 0, 24))
|
||||
|
||||
[prop: string]: (state: State) => State;
|
||||
>prop : Symbol(prop, Decl(index.tsx, 1, 5))
|
||||
>state : Symbol(state, Decl(index.tsx, 1, 21))
|
||||
>State : Symbol(State, Decl(index.tsx, 0, 24))
|
||||
>State : Symbol(State, Decl(index.tsx, 0, 24))
|
||||
}
|
||||
|
||||
interface Options<State, Actions> {
|
||||
>Options : Symbol(Options, Decl(index.tsx, 2, 1))
|
||||
>State : Symbol(State, Decl(index.tsx, 4, 18))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 4, 24))
|
||||
|
||||
state?: State;
|
||||
>state : Symbol(Options.state, Decl(index.tsx, 4, 35))
|
||||
>State : Symbol(State, Decl(index.tsx, 4, 18))
|
||||
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
>view : Symbol(Options.view, Decl(index.tsx, 5, 18))
|
||||
>state : Symbol(state, Decl(index.tsx, 6, 12))
|
||||
>State : Symbol(State, Decl(index.tsx, 4, 18))
|
||||
>actions : Symbol(actions, Decl(index.tsx, 6, 25))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 4, 24))
|
||||
|
||||
actions: string | Actions;
|
||||
>actions : Symbol(Options.actions, Decl(index.tsx, 6, 51))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 4, 24))
|
||||
}
|
||||
|
||||
declare function app<State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>): void;
|
||||
>app : Symbol(app, Decl(index.tsx, 8, 1))
|
||||
>State : Symbol(State, Decl(index.tsx, 10, 21))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 10, 27))
|
||||
>ActionsObject : Symbol(ActionsObject, Decl(index.tsx, 0, 0))
|
||||
>State : Symbol(State, Decl(index.tsx, 10, 21))
|
||||
>obj : Symbol(obj, Decl(index.tsx, 10, 66))
|
||||
>Options : Symbol(Options, Decl(index.tsx, 2, 1))
|
||||
>State : Symbol(State, Decl(index.tsx, 10, 21))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 10, 27))
|
||||
|
||||
app({
|
||||
>app : Symbol(app, Decl(index.tsx, 8, 1))
|
||||
|
||||
state: 100,
|
||||
>state : Symbol(state, Decl(index.tsx, 12, 5))
|
||||
|
||||
actions: {
|
||||
>actions : Symbol(actions, Decl(index.tsx, 13, 15))
|
||||
|
||||
foo: s => s // Should be typed number => number
|
||||
>foo : Symbol(foo, Decl(index.tsx, 14, 14))
|
||||
>s : Symbol(s, Decl(index.tsx, 15, 12))
|
||||
>s : Symbol(s, Decl(index.tsx, 15, 12))
|
||||
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
>view : Symbol(view, Decl(index.tsx, 16, 6))
|
||||
>s : Symbol(s, Decl(index.tsx, 17, 11))
|
||||
>a : Symbol(a, Decl(index.tsx, 17, 13))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
});
|
||||
|
||||
|
||||
interface Bar {
|
||||
>Bar : Symbol(Bar, Decl(index.tsx, 18, 3))
|
||||
|
||||
bar: (a: number) => void;
|
||||
>bar : Symbol(Bar.bar, Decl(index.tsx, 21, 15))
|
||||
>a : Symbol(a, Decl(index.tsx, 22, 10))
|
||||
}
|
||||
|
||||
declare function foo<T extends Bar>(x: string | T): T;
|
||||
>foo : Symbol(foo, Decl(index.tsx, 23, 1))
|
||||
>T : Symbol(T, Decl(index.tsx, 25, 21))
|
||||
>Bar : Symbol(Bar, Decl(index.tsx, 18, 3))
|
||||
>x : Symbol(x, Decl(index.tsx, 25, 36))
|
||||
>T : Symbol(T, Decl(index.tsx, 25, 21))
|
||||
>T : Symbol(T, Decl(index.tsx, 25, 21))
|
||||
|
||||
const y = foo({
|
||||
>y : Symbol(y, Decl(index.tsx, 27, 5))
|
||||
>foo : Symbol(foo, Decl(index.tsx, 23, 1))
|
||||
|
||||
bar(x) { // Should be typed number => void
|
||||
>bar : Symbol(bar, Decl(index.tsx, 27, 15))
|
||||
>x : Symbol(x, Decl(index.tsx, 28, 8))
|
||||
}
|
||||
});
|
||||
|
||||
interface Options2<State, Actions> {
|
||||
>Options2 : Symbol(Options2, Decl(index.tsx, 30, 3))
|
||||
>State : Symbol(State, Decl(index.tsx, 32, 19))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 32, 25))
|
||||
|
||||
state?: State;
|
||||
>state : Symbol(Options2.state, Decl(index.tsx, 32, 36))
|
||||
>State : Symbol(State, Decl(index.tsx, 32, 19))
|
||||
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
>view : Symbol(Options2.view, Decl(index.tsx, 33, 18))
|
||||
>state : Symbol(state, Decl(index.tsx, 34, 12))
|
||||
>State : Symbol(State, Decl(index.tsx, 32, 19))
|
||||
>actions : Symbol(actions, Decl(index.tsx, 34, 25))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 32, 25))
|
||||
|
||||
actions?: Actions;
|
||||
>actions : Symbol(Options2.actions, Decl(index.tsx, 34, 51))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 32, 25))
|
||||
}
|
||||
|
||||
declare function app2<State, Actions extends ActionsObject<State>>(obj: Options2<State, Actions>): void;
|
||||
>app2 : Symbol(app2, Decl(index.tsx, 36, 1))
|
||||
>State : Symbol(State, Decl(index.tsx, 38, 22))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 38, 28))
|
||||
>ActionsObject : Symbol(ActionsObject, Decl(index.tsx, 0, 0))
|
||||
>State : Symbol(State, Decl(index.tsx, 38, 22))
|
||||
>obj : Symbol(obj, Decl(index.tsx, 38, 67))
|
||||
>Options2 : Symbol(Options2, Decl(index.tsx, 30, 3))
|
||||
>State : Symbol(State, Decl(index.tsx, 38, 22))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 38, 28))
|
||||
|
||||
app2({
|
||||
>app2 : Symbol(app2, Decl(index.tsx, 36, 1))
|
||||
|
||||
state: 100,
|
||||
>state : Symbol(state, Decl(index.tsx, 40, 6))
|
||||
|
||||
actions: {
|
||||
>actions : Symbol(actions, Decl(index.tsx, 41, 15))
|
||||
|
||||
foo: s => s // Should be typed number => number
|
||||
>foo : Symbol(foo, Decl(index.tsx, 42, 14))
|
||||
>s : Symbol(s, Decl(index.tsx, 43, 12))
|
||||
>s : Symbol(s, Decl(index.tsx, 43, 12))
|
||||
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
>view : Symbol(view, Decl(index.tsx, 44, 6))
|
||||
>s : Symbol(s, Decl(index.tsx, 45, 11))
|
||||
>a : Symbol(a, Decl(index.tsx, 45, 13))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
});
|
||||
|
||||
|
||||
type ActionsArray<State> = ((state: State) => State)[];
|
||||
>ActionsArray : Symbol(ActionsArray, Decl(index.tsx, 46, 3))
|
||||
>State : Symbol(State, Decl(index.tsx, 49, 18))
|
||||
>state : Symbol(state, Decl(index.tsx, 49, 29))
|
||||
>State : Symbol(State, Decl(index.tsx, 49, 18))
|
||||
>State : Symbol(State, Decl(index.tsx, 49, 18))
|
||||
|
||||
declare function app3<State, Actions extends ActionsArray<State>>(obj: Options<State, Actions>): void;
|
||||
>app3 : Symbol(app3, Decl(index.tsx, 49, 55))
|
||||
>State : Symbol(State, Decl(index.tsx, 51, 22))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 51, 28))
|
||||
>ActionsArray : Symbol(ActionsArray, Decl(index.tsx, 46, 3))
|
||||
>State : Symbol(State, Decl(index.tsx, 51, 22))
|
||||
>obj : Symbol(obj, Decl(index.tsx, 51, 66))
|
||||
>Options : Symbol(Options, Decl(index.tsx, 2, 1))
|
||||
>State : Symbol(State, Decl(index.tsx, 51, 22))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 51, 28))
|
||||
|
||||
app3({
|
||||
>app3 : Symbol(app3, Decl(index.tsx, 49, 55))
|
||||
|
||||
state: 100,
|
||||
>state : Symbol(state, Decl(index.tsx, 53, 6))
|
||||
|
||||
actions: [
|
||||
>actions : Symbol(actions, Decl(index.tsx, 54, 15))
|
||||
|
||||
s => s // Should be typed number => number
|
||||
>s : Symbol(s, Decl(index.tsx, 55, 14))
|
||||
>s : Symbol(s, Decl(index.tsx, 55, 14))
|
||||
|
||||
],
|
||||
view: (s, a) => undefined as any,
|
||||
>view : Symbol(view, Decl(index.tsx, 57, 6))
|
||||
>s : Symbol(s, Decl(index.tsx, 58, 11))
|
||||
>a : Symbol(a, Decl(index.tsx, 58, 13))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
});
|
||||
|
||||
namespace JSX {
|
||||
>JSX : Symbol(JSX, Decl(index.tsx, 59, 3))
|
||||
|
||||
export interface Element {}
|
||||
>Element : Symbol(Element, Decl(index.tsx, 61, 15))
|
||||
|
||||
export interface IntrinsicElements {}
|
||||
>IntrinsicElements : Symbol(IntrinsicElements, Decl(index.tsx, 62, 31))
|
||||
}
|
||||
|
||||
interface ActionsObjectOr<State> {
|
||||
>ActionsObjectOr : Symbol(ActionsObjectOr, Decl(index.tsx, 64, 1))
|
||||
>State : Symbol(State, Decl(index.tsx, 66, 26))
|
||||
|
||||
[prop: string]: ((state: State) => State) | State;
|
||||
>prop : Symbol(prop, Decl(index.tsx, 67, 5))
|
||||
>state : Symbol(state, Decl(index.tsx, 67, 22))
|
||||
>State : Symbol(State, Decl(index.tsx, 66, 26))
|
||||
>State : Symbol(State, Decl(index.tsx, 66, 26))
|
||||
>State : Symbol(State, Decl(index.tsx, 66, 26))
|
||||
}
|
||||
|
||||
declare function App4<State, Actions extends ActionsObjectOr<State>>(props: Options<State, Actions>["actions"] & { state: State }): JSX.Element;
|
||||
>App4 : Symbol(App4, Decl(index.tsx, 68, 1))
|
||||
>State : Symbol(State, Decl(index.tsx, 70, 22))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 70, 28))
|
||||
>ActionsObjectOr : Symbol(ActionsObjectOr, Decl(index.tsx, 64, 1))
|
||||
>State : Symbol(State, Decl(index.tsx, 70, 22))
|
||||
>props : Symbol(props, Decl(index.tsx, 70, 69))
|
||||
>Options : Symbol(Options, Decl(index.tsx, 2, 1))
|
||||
>State : Symbol(State, Decl(index.tsx, 70, 22))
|
||||
>Actions : Symbol(Actions, Decl(index.tsx, 70, 28))
|
||||
>state : Symbol(state, Decl(index.tsx, 70, 114))
|
||||
>State : Symbol(State, Decl(index.tsx, 70, 22))
|
||||
>JSX : Symbol(JSX, Decl(index.tsx, 59, 3))
|
||||
>Element : Symbol(JSX.Element, Decl(index.tsx, 61, 15))
|
||||
|
||||
const a = <App4 state={100} foo={s => s} />; // TODO: should be number => number, but JSX resolution is missing an inferential pass
|
||||
>a : Symbol(a, Decl(index.tsx, 72, 5))
|
||||
>App4 : Symbol(App4, Decl(index.tsx, 68, 1))
|
||||
>state : Symbol(state, Decl(index.tsx, 72, 15))
|
||||
>foo : Symbol(foo, Decl(index.tsx, 72, 27))
|
||||
>s : Symbol(s, Decl(index.tsx, 72, 33))
|
||||
>s : Symbol(s, Decl(index.tsx, 72, 33))
|
||||
|
||||
@ -0,0 +1,261 @@
|
||||
=== tests/cases/compiler/index.tsx ===
|
||||
interface ActionsObject<State> {
|
||||
>ActionsObject : ActionsObject<State>
|
||||
>State : State
|
||||
|
||||
[prop: string]: (state: State) => State;
|
||||
>prop : string
|
||||
>state : State
|
||||
>State : State
|
||||
>State : State
|
||||
}
|
||||
|
||||
interface Options<State, Actions> {
|
||||
>Options : Options<State, Actions>
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
|
||||
state?: State;
|
||||
>state : State | undefined
|
||||
>State : State
|
||||
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
>view : ((state: State, actions: Actions) => any) | undefined
|
||||
>state : State
|
||||
>State : State
|
||||
>actions : Actions
|
||||
>Actions : Actions
|
||||
|
||||
actions: string | Actions;
|
||||
>actions : string | Actions
|
||||
>Actions : Actions
|
||||
}
|
||||
|
||||
declare function app<State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>): void;
|
||||
>app : <State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>) => void
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
>ActionsObject : ActionsObject<State>
|
||||
>State : State
|
||||
>obj : Options<State, Actions>
|
||||
>Options : Options<State, Actions>
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
|
||||
app({
|
||||
>app({ state: 100, actions: { foo: s => s // Should be typed number => number }, view: (s, a) => undefined as any,}) : void
|
||||
>app : <State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>) => void
|
||||
>{ state: 100, actions: { foo: s => s // Should be typed number => number }, view: (s, a) => undefined as any,} : { state: number; actions: { foo: (s: number) => number; }; view: (s: number, a: ActionsObject<number>) => any; }
|
||||
|
||||
state: 100,
|
||||
>state : number
|
||||
>100 : 100
|
||||
|
||||
actions: {
|
||||
>actions : { foo: (s: number) => number; }
|
||||
>{ foo: s => s // Should be typed number => number } : { foo: (s: number) => number; }
|
||||
|
||||
foo: s => s // Should be typed number => number
|
||||
>foo : (s: number) => number
|
||||
>s => s : (s: number) => number
|
||||
>s : number
|
||||
>s : number
|
||||
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
>view : (s: number, a: ActionsObject<number>) => any
|
||||
>(s, a) => undefined as any : (s: number, a: ActionsObject<number>) => any
|
||||
>s : number
|
||||
>a : ActionsObject<number>
|
||||
>undefined as any : any
|
||||
>undefined : undefined
|
||||
|
||||
});
|
||||
|
||||
|
||||
interface Bar {
|
||||
>Bar : Bar
|
||||
|
||||
bar: (a: number) => void;
|
||||
>bar : (a: number) => void
|
||||
>a : number
|
||||
}
|
||||
|
||||
declare function foo<T extends Bar>(x: string | T): T;
|
||||
>foo : <T extends Bar>(x: string | T) => T
|
||||
>T : T
|
||||
>Bar : Bar
|
||||
>x : string | T
|
||||
>T : T
|
||||
>T : T
|
||||
|
||||
const y = foo({
|
||||
>y : { bar(x: number): void; }
|
||||
>foo({ bar(x) { // Should be typed number => void }}) : { bar(x: number): void; }
|
||||
>foo : <T extends Bar>(x: string | T) => T
|
||||
>{ bar(x) { // Should be typed number => void }} : { bar(x: number): void; }
|
||||
|
||||
bar(x) { // Should be typed number => void
|
||||
>bar : (x: number) => void
|
||||
>x : number
|
||||
}
|
||||
});
|
||||
|
||||
interface Options2<State, Actions> {
|
||||
>Options2 : Options2<State, Actions>
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
|
||||
state?: State;
|
||||
>state : State | undefined
|
||||
>State : State
|
||||
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
>view : ((state: State, actions: Actions) => any) | undefined
|
||||
>state : State
|
||||
>State : State
|
||||
>actions : Actions
|
||||
>Actions : Actions
|
||||
|
||||
actions?: Actions;
|
||||
>actions : Actions | undefined
|
||||
>Actions : Actions
|
||||
}
|
||||
|
||||
declare function app2<State, Actions extends ActionsObject<State>>(obj: Options2<State, Actions>): void;
|
||||
>app2 : <State, Actions extends ActionsObject<State>>(obj: Options2<State, Actions>) => void
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
>ActionsObject : ActionsObject<State>
|
||||
>State : State
|
||||
>obj : Options2<State, Actions>
|
||||
>Options2 : Options2<State, Actions>
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
|
||||
app2({
|
||||
>app2({ state: 100, actions: { foo: s => s // Should be typed number => number }, view: (s, a) => undefined as any,}) : void
|
||||
>app2 : <State, Actions extends ActionsObject<State>>(obj: Options2<State, Actions>) => void
|
||||
>{ state: 100, actions: { foo: s => s // Should be typed number => number }, view: (s, a) => undefined as any,} : { state: number; actions: { foo: (s: number) => number; }; view: (s: number, a: ActionsObject<number>) => any; }
|
||||
|
||||
state: 100,
|
||||
>state : number
|
||||
>100 : 100
|
||||
|
||||
actions: {
|
||||
>actions : { foo: (s: number) => number; }
|
||||
>{ foo: s => s // Should be typed number => number } : { foo: (s: number) => number; }
|
||||
|
||||
foo: s => s // Should be typed number => number
|
||||
>foo : (s: number) => number
|
||||
>s => s : (s: number) => number
|
||||
>s : number
|
||||
>s : number
|
||||
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
>view : (s: number, a: ActionsObject<number>) => any
|
||||
>(s, a) => undefined as any : (s: number, a: ActionsObject<number>) => any
|
||||
>s : number
|
||||
>a : ActionsObject<number>
|
||||
>undefined as any : any
|
||||
>undefined : undefined
|
||||
|
||||
});
|
||||
|
||||
|
||||
type ActionsArray<State> = ((state: State) => State)[];
|
||||
>ActionsArray : ((state: State) => State)[]
|
||||
>State : State
|
||||
>state : State
|
||||
>State : State
|
||||
>State : State
|
||||
|
||||
declare function app3<State, Actions extends ActionsArray<State>>(obj: Options<State, Actions>): void;
|
||||
>app3 : <State, Actions extends ((state: State) => State)[]>(obj: Options<State, Actions>) => void
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
>ActionsArray : ((state: State) => State)[]
|
||||
>State : State
|
||||
>obj : Options<State, Actions>
|
||||
>Options : Options<State, Actions>
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
|
||||
app3({
|
||||
>app3({ state: 100, actions: [ s => s // Should be typed number => number ], view: (s, a) => undefined as any,}) : void
|
||||
>app3 : <State, Actions extends ((state: State) => State)[]>(obj: Options<State, Actions>) => void
|
||||
>{ state: 100, actions: [ s => s // Should be typed number => number ], view: (s, a) => undefined as any,} : { state: number; actions: ((s: number) => number)[]; view: (s: number, a: ((state: number) => number)[]) => any; }
|
||||
|
||||
state: 100,
|
||||
>state : number
|
||||
>100 : 100
|
||||
|
||||
actions: [
|
||||
>actions : ((s: number) => number)[]
|
||||
>[ s => s // Should be typed number => number ] : ((s: number) => number)[]
|
||||
|
||||
s => s // Should be typed number => number
|
||||
>s => s : (s: number) => number
|
||||
>s : number
|
||||
>s : number
|
||||
|
||||
],
|
||||
view: (s, a) => undefined as any,
|
||||
>view : (s: number, a: ((state: number) => number)[]) => any
|
||||
>(s, a) => undefined as any : (s: number, a: ((state: number) => number)[]) => any
|
||||
>s : number
|
||||
>a : ((state: number) => number)[]
|
||||
>undefined as any : any
|
||||
>undefined : undefined
|
||||
|
||||
});
|
||||
|
||||
namespace JSX {
|
||||
>JSX : any
|
||||
|
||||
export interface Element {}
|
||||
>Element : Element
|
||||
|
||||
export interface IntrinsicElements {}
|
||||
>IntrinsicElements : IntrinsicElements
|
||||
}
|
||||
|
||||
interface ActionsObjectOr<State> {
|
||||
>ActionsObjectOr : ActionsObjectOr<State>
|
||||
>State : State
|
||||
|
||||
[prop: string]: ((state: State) => State) | State;
|
||||
>prop : string
|
||||
>state : State
|
||||
>State : State
|
||||
>State : State
|
||||
>State : State
|
||||
}
|
||||
|
||||
declare function App4<State, Actions extends ActionsObjectOr<State>>(props: Options<State, Actions>["actions"] & { state: State }): JSX.Element;
|
||||
>App4 : <State, Actions extends ActionsObjectOr<State>>(props: (string & { state: State; }) | (Actions & { state: State; })) => JSX.Element
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
>ActionsObjectOr : ActionsObjectOr<State>
|
||||
>State : State
|
||||
>props : (string & { state: State; }) | (Actions & { state: State; })
|
||||
>Options : Options<State, Actions>
|
||||
>State : State
|
||||
>Actions : Actions
|
||||
>state : State
|
||||
>State : State
|
||||
>JSX : any
|
||||
>Element : JSX.Element
|
||||
|
||||
const a = <App4 state={100} foo={s => s} />; // TODO: should be number => number, but JSX resolution is missing an inferential pass
|
||||
>a : JSX.Element
|
||||
><App4 state={100} foo={s => s} /> : JSX.Element
|
||||
>App4 : <State, Actions extends ActionsObjectOr<State>>(props: (string & { state: State; }) | (Actions & { state: State; })) => JSX.Element
|
||||
>state : number
|
||||
>100 : 100
|
||||
>foo : (s: any) => any
|
||||
>s => s : (s: any) => any
|
||||
>s : any
|
||||
>s : any
|
||||
|
||||
77
tests/cases/compiler/contextualTypingOfOptionalMembers.tsx
Normal file
77
tests/cases/compiler/contextualTypingOfOptionalMembers.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
// @noImplicitAny: true
|
||||
// @strictNullChecks: true
|
||||
// @jsx: preserve
|
||||
// @filename: index.tsx
|
||||
interface ActionsObject<State> {
|
||||
[prop: string]: (state: State) => State;
|
||||
}
|
||||
|
||||
interface Options<State, Actions> {
|
||||
state?: State;
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
actions: string | Actions;
|
||||
}
|
||||
|
||||
declare function app<State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>): void;
|
||||
|
||||
app({
|
||||
state: 100,
|
||||
actions: {
|
||||
foo: s => s // Should be typed number => number
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
});
|
||||
|
||||
|
||||
interface Bar {
|
||||
bar: (a: number) => void;
|
||||
}
|
||||
|
||||
declare function foo<T extends Bar>(x: string | T): T;
|
||||
|
||||
const y = foo({
|
||||
bar(x) { // Should be typed number => void
|
||||
}
|
||||
});
|
||||
|
||||
interface Options2<State, Actions> {
|
||||
state?: State;
|
||||
view?: (state: State, actions: Actions) => any;
|
||||
actions?: Actions;
|
||||
}
|
||||
|
||||
declare function app2<State, Actions extends ActionsObject<State>>(obj: Options2<State, Actions>): void;
|
||||
|
||||
app2({
|
||||
state: 100,
|
||||
actions: {
|
||||
foo: s => s // Should be typed number => number
|
||||
},
|
||||
view: (s, a) => undefined as any,
|
||||
});
|
||||
|
||||
|
||||
type ActionsArray<State> = ((state: State) => State)[];
|
||||
|
||||
declare function app3<State, Actions extends ActionsArray<State>>(obj: Options<State, Actions>): void;
|
||||
|
||||
app3({
|
||||
state: 100,
|
||||
actions: [
|
||||
s => s // Should be typed number => number
|
||||
],
|
||||
view: (s, a) => undefined as any,
|
||||
});
|
||||
|
||||
namespace JSX {
|
||||
export interface Element {}
|
||||
export interface IntrinsicElements {}
|
||||
}
|
||||
|
||||
interface ActionsObjectOr<State> {
|
||||
[prop: string]: ((state: State) => State) | State;
|
||||
}
|
||||
|
||||
declare function App4<State, Actions extends ActionsObjectOr<State>>(props: Options<State, Actions>["actions"] & { state: State }): JSX.Element;
|
||||
|
||||
const a = <App4 state={100} foo={s => s} />; // TODO: should be number => number, but JSX resolution is missing an inferential pass
|
||||
Loading…
x
Reference in New Issue
Block a user