mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Do not consider binding patterns in contextual types for return type inference where all the signature type parameters have defaults (#39081)
This commit is contained in:
parent
785e44704c
commit
d1ebf126d2
@ -22855,14 +22855,14 @@ namespace ts {
|
||||
// the contextual type of an initializer expression is the type implied by the binding pattern.
|
||||
// Otherwise, in a binding pattern inside a variable or parameter declaration,
|
||||
// the contextual type of an initializer expression is the type annotation of the containing declaration, if present.
|
||||
function getContextualTypeForInitializerExpression(node: Expression): Type | undefined {
|
||||
function getContextualTypeForInitializerExpression(node: Expression, contextFlags?: ContextFlags): Type | undefined {
|
||||
const declaration = <VariableLikeDeclaration>node.parent;
|
||||
if (hasInitializer(declaration) && node === declaration.initializer) {
|
||||
const result = getContextualTypeForVariableLikeDeclaration(declaration);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
if (isBindingPattern(declaration.name)) { // This is less a contextual type and more an implied shape - in some cases, this may be undesirable
|
||||
if (!(contextFlags! & ContextFlags.SkipBindingPatterns) && isBindingPattern(declaration.name)) { // This is less a contextual type and more an implied shape - in some cases, this may be undesirable
|
||||
return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false);
|
||||
}
|
||||
}
|
||||
@ -22889,8 +22889,8 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getContextualTypeForAwaitOperand(node: AwaitExpression): Type | undefined {
|
||||
const contextualType = getContextualType(node);
|
||||
function getContextualTypeForAwaitOperand(node: AwaitExpression, contextFlags?: ContextFlags): Type | undefined {
|
||||
const contextualType = getContextualType(node, contextFlags);
|
||||
if (contextualType) {
|
||||
const contextualAwaitedType = getAwaitedType(contextualType);
|
||||
return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]);
|
||||
@ -23355,14 +23355,14 @@ namespace ts {
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.BindingElement:
|
||||
return getContextualTypeForInitializerExpression(node);
|
||||
return getContextualTypeForInitializerExpression(node, contextFlags);
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.ReturnStatement:
|
||||
return getContextualTypeForReturnExpression(node);
|
||||
case SyntaxKind.YieldExpression:
|
||||
return getContextualTypeForYieldOperand(<YieldExpression>parent);
|
||||
case SyntaxKind.AwaitExpression:
|
||||
return getContextualTypeForAwaitOperand(<AwaitExpression>parent);
|
||||
return getContextualTypeForAwaitOperand(<AwaitExpression>parent, contextFlags);
|
||||
case SyntaxKind.CallExpression:
|
||||
if ((<CallExpression>parent).expression.kind === SyntaxKind.ImportKeyword) {
|
||||
return stringType;
|
||||
@ -25700,7 +25700,7 @@ namespace ts {
|
||||
// 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type of 'f' to the
|
||||
// return type of 'wrap'.
|
||||
if (node.kind !== SyntaxKind.Decorator) {
|
||||
const contextualType = getContextualType(node);
|
||||
const contextualType = getContextualType(node, every(signature.typeParameters, p => !!getDefaultFromTypeParameter(p)) ? ContextFlags.SkipBindingPatterns : ContextFlags.None);
|
||||
if (contextualType) {
|
||||
// We clone the inference context to avoid disturbing a resolution in progress for an
|
||||
// outer call expression. Effectively we just want a snapshot of whatever has been
|
||||
|
||||
@ -4136,7 +4136,7 @@ namespace ts {
|
||||
Signature = 1 << 0, // Obtaining contextual signature
|
||||
NoConstraints = 1 << 1, // Don't obtain type variable constraints
|
||||
Completions = 1 << 2, // Ignore inference to current node and parent nodes out to the containing call for completions
|
||||
|
||||
SkipBindingPatterns = 1 << 3, // Ignore contextual types applied by binding patterns
|
||||
}
|
||||
|
||||
// NOTE: If modifying this enum, must modify `TypeFormatFlags` too!
|
||||
|
||||
@ -0,0 +1,97 @@
|
||||
//// [destructureOfVariableSameAsShorthand.ts]
|
||||
// https://github.com/microsoft/TypeScript/issues/38969
|
||||
interface AxiosResponse<T = never> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
declare function get<T = never, R = AxiosResponse<T>>(): Promise<R>;
|
||||
|
||||
async function main() {
|
||||
// These work examples as expected
|
||||
get().then((response) => {
|
||||
// body is never
|
||||
const body = response.data;
|
||||
})
|
||||
get().then(({ data }) => {
|
||||
// data is never
|
||||
})
|
||||
const response = await get()
|
||||
// body is never
|
||||
const body = response.data;
|
||||
// data is never
|
||||
const { data } = await get<never>();
|
||||
|
||||
// The following did not work as expected.
|
||||
// shouldBeNever should be never, but was any
|
||||
const { data: shouldBeNever } = await get();
|
||||
}
|
||||
|
||||
//// [destructureOfVariableSameAsShorthand.js]
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (_) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
function main() {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var response, body, data, shouldBeNever;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
// These work examples as expected
|
||||
get().then(function (response) {
|
||||
// body is never
|
||||
var body = response.data;
|
||||
});
|
||||
get().then(function (_a) {
|
||||
var data = _a.data;
|
||||
// data is never
|
||||
});
|
||||
return [4 /*yield*/, get()
|
||||
// body is never
|
||||
];
|
||||
case 1:
|
||||
response = _a.sent();
|
||||
body = response.data;
|
||||
return [4 /*yield*/, get()];
|
||||
case 2:
|
||||
data = (_a.sent()).data;
|
||||
return [4 /*yield*/, get()];
|
||||
case 3:
|
||||
shouldBeNever = (_a.sent()).data;
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
=== tests/cases/compiler/destructureOfVariableSameAsShorthand.ts ===
|
||||
// https://github.com/microsoft/TypeScript/issues/38969
|
||||
interface AxiosResponse<T = never> {
|
||||
>AxiosResponse : Symbol(AxiosResponse, Decl(destructureOfVariableSameAsShorthand.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(destructureOfVariableSameAsShorthand.ts, 1, 24))
|
||||
|
||||
data: T;
|
||||
>data : Symbol(AxiosResponse.data, Decl(destructureOfVariableSameAsShorthand.ts, 1, 36))
|
||||
>T : Symbol(T, Decl(destructureOfVariableSameAsShorthand.ts, 1, 24))
|
||||
}
|
||||
|
||||
declare function get<T = never, R = AxiosResponse<T>>(): Promise<R>;
|
||||
>get : Symbol(get, Decl(destructureOfVariableSameAsShorthand.ts, 3, 1))
|
||||
>T : Symbol(T, Decl(destructureOfVariableSameAsShorthand.ts, 5, 21))
|
||||
>R : Symbol(R, Decl(destructureOfVariableSameAsShorthand.ts, 5, 31))
|
||||
>AxiosResponse : Symbol(AxiosResponse, Decl(destructureOfVariableSameAsShorthand.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(destructureOfVariableSameAsShorthand.ts, 5, 21))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
|
||||
>R : Symbol(R, Decl(destructureOfVariableSameAsShorthand.ts, 5, 31))
|
||||
|
||||
async function main() {
|
||||
>main : Symbol(main, Decl(destructureOfVariableSameAsShorthand.ts, 5, 68))
|
||||
|
||||
// These work examples as expected
|
||||
get().then((response) => {
|
||||
>get().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
|
||||
>get : Symbol(get, Decl(destructureOfVariableSameAsShorthand.ts, 3, 1))
|
||||
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
|
||||
>response : Symbol(response, Decl(destructureOfVariableSameAsShorthand.ts, 9, 16))
|
||||
|
||||
// body is never
|
||||
const body = response.data;
|
||||
>body : Symbol(body, Decl(destructureOfVariableSameAsShorthand.ts, 11, 13))
|
||||
>response.data : Symbol(AxiosResponse.data, Decl(destructureOfVariableSameAsShorthand.ts, 1, 36))
|
||||
>response : Symbol(response, Decl(destructureOfVariableSameAsShorthand.ts, 9, 16))
|
||||
>data : Symbol(AxiosResponse.data, Decl(destructureOfVariableSameAsShorthand.ts, 1, 36))
|
||||
|
||||
})
|
||||
get().then(({ data }) => {
|
||||
>get().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
|
||||
>get : Symbol(get, Decl(destructureOfVariableSameAsShorthand.ts, 3, 1))
|
||||
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
|
||||
>data : Symbol(data, Decl(destructureOfVariableSameAsShorthand.ts, 13, 17))
|
||||
|
||||
// data is never
|
||||
})
|
||||
const response = await get()
|
||||
>response : Symbol(response, Decl(destructureOfVariableSameAsShorthand.ts, 16, 9))
|
||||
>get : Symbol(get, Decl(destructureOfVariableSameAsShorthand.ts, 3, 1))
|
||||
|
||||
// body is never
|
||||
const body = response.data;
|
||||
>body : Symbol(body, Decl(destructureOfVariableSameAsShorthand.ts, 18, 9))
|
||||
>response.data : Symbol(AxiosResponse.data, Decl(destructureOfVariableSameAsShorthand.ts, 1, 36))
|
||||
>response : Symbol(response, Decl(destructureOfVariableSameAsShorthand.ts, 16, 9))
|
||||
>data : Symbol(AxiosResponse.data, Decl(destructureOfVariableSameAsShorthand.ts, 1, 36))
|
||||
|
||||
// data is never
|
||||
const { data } = await get<never>();
|
||||
>data : Symbol(data, Decl(destructureOfVariableSameAsShorthand.ts, 20, 11))
|
||||
>get : Symbol(get, Decl(destructureOfVariableSameAsShorthand.ts, 3, 1))
|
||||
|
||||
// The following did not work as expected.
|
||||
// shouldBeNever should be never, but was any
|
||||
const { data: shouldBeNever } = await get();
|
||||
>data : Symbol(AxiosResponse.data, Decl(destructureOfVariableSameAsShorthand.ts, 1, 36))
|
||||
>shouldBeNever : Symbol(shouldBeNever, Decl(destructureOfVariableSameAsShorthand.ts, 24, 11))
|
||||
>get : Symbol(get, Decl(destructureOfVariableSameAsShorthand.ts, 3, 1))
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
=== tests/cases/compiler/destructureOfVariableSameAsShorthand.ts ===
|
||||
// https://github.com/microsoft/TypeScript/issues/38969
|
||||
interface AxiosResponse<T = never> {
|
||||
data: T;
|
||||
>data : T
|
||||
}
|
||||
|
||||
declare function get<T = never, R = AxiosResponse<T>>(): Promise<R>;
|
||||
>get : <T = never, R = AxiosResponse<T>>() => Promise<R>
|
||||
|
||||
async function main() {
|
||||
>main : () => Promise<void>
|
||||
|
||||
// These work examples as expected
|
||||
get().then((response) => {
|
||||
>get().then((response) => { // body is never const body = response.data; }) : Promise<void>
|
||||
>get().then : <TResult1 = AxiosResponse<never>, TResult2 = never>(onfulfilled?: (value: AxiosResponse<never>) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
|
||||
>get() : Promise<AxiosResponse<never>>
|
||||
>get : <T = never, R = AxiosResponse<T>>() => Promise<R>
|
||||
>then : <TResult1 = AxiosResponse<never>, TResult2 = never>(onfulfilled?: (value: AxiosResponse<never>) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
|
||||
>(response) => { // body is never const body = response.data; } : (response: AxiosResponse<never>) => void
|
||||
>response : AxiosResponse<never>
|
||||
|
||||
// body is never
|
||||
const body = response.data;
|
||||
>body : never
|
||||
>response.data : never
|
||||
>response : AxiosResponse<never>
|
||||
>data : never
|
||||
|
||||
})
|
||||
get().then(({ data }) => {
|
||||
>get().then(({ data }) => { // data is never }) : Promise<void>
|
||||
>get().then : <TResult1 = AxiosResponse<never>, TResult2 = never>(onfulfilled?: (value: AxiosResponse<never>) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
|
||||
>get() : Promise<AxiosResponse<never>>
|
||||
>get : <T = never, R = AxiosResponse<T>>() => Promise<R>
|
||||
>then : <TResult1 = AxiosResponse<never>, TResult2 = never>(onfulfilled?: (value: AxiosResponse<never>) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
|
||||
>({ data }) => { // data is never } : ({ data }: AxiosResponse<never>) => void
|
||||
>data : never
|
||||
|
||||
// data is never
|
||||
})
|
||||
const response = await get()
|
||||
>response : AxiosResponse<never>
|
||||
>await get() : AxiosResponse<never>
|
||||
>get() : Promise<AxiosResponse<never>>
|
||||
>get : <T = never, R = AxiosResponse<T>>() => Promise<R>
|
||||
|
||||
// body is never
|
||||
const body = response.data;
|
||||
>body : never
|
||||
>response.data : never
|
||||
>response : AxiosResponse<never>
|
||||
>data : never
|
||||
|
||||
// data is never
|
||||
const { data } = await get<never>();
|
||||
>data : never
|
||||
>await get<never>() : AxiosResponse<never>
|
||||
>get<never>() : Promise<AxiosResponse<never>>
|
||||
>get : <T = never, R = AxiosResponse<T>>() => Promise<R>
|
||||
|
||||
// The following did not work as expected.
|
||||
// shouldBeNever should be never, but was any
|
||||
const { data: shouldBeNever } = await get();
|
||||
>data : any
|
||||
>shouldBeNever : never
|
||||
>await get() : AxiosResponse<never>
|
||||
>get() : Promise<AxiosResponse<never>>
|
||||
>get : <T = never, R = AxiosResponse<T>>() => Promise<R>
|
||||
}
|
||||
26
tests/cases/compiler/destructureOfVariableSameAsShorthand.ts
Normal file
26
tests/cases/compiler/destructureOfVariableSameAsShorthand.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// https://github.com/microsoft/TypeScript/issues/38969
|
||||
interface AxiosResponse<T = never> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
declare function get<T = never, R = AxiosResponse<T>>(): Promise<R>;
|
||||
|
||||
async function main() {
|
||||
// These work examples as expected
|
||||
get().then((response) => {
|
||||
// body is never
|
||||
const body = response.data;
|
||||
})
|
||||
get().then(({ data }) => {
|
||||
// data is never
|
||||
})
|
||||
const response = await get()
|
||||
// body is never
|
||||
const body = response.data;
|
||||
// data is never
|
||||
const { data } = await get<never>();
|
||||
|
||||
// The following did not work as expected.
|
||||
// shouldBeNever should be never, but was any
|
||||
const { data: shouldBeNever } = await get();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user