mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Don't widen when type parameter occurs at top level in type predicate (#52031)
This commit is contained in:
@@ -23465,6 +23465,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
type.flags & TypeFlags.Conditional && (getTrueTypeFromConditionalType(type as ConditionalType) === typeParameter || getFalseTypeFromConditionalType(type as ConditionalType) === typeParameter));
|
||||
}
|
||||
|
||||
function isTypeParameterAtTopLevelInReturnType(signature: Signature, typeParameter: TypeParameter) {
|
||||
const typePredicate = getTypePredicateOfSignature(signature);
|
||||
return typePredicate ? !!typePredicate.type && isTypeParameterAtTopLevel(typePredicate.type, typeParameter) :
|
||||
isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), typeParameter);
|
||||
}
|
||||
|
||||
/** Create an object with properties named in the string literal type. Every property has type `any` */
|
||||
function createEmptyObjectTypeFromStringLiteral(type: Type) {
|
||||
const members = createSymbolTable();
|
||||
@@ -24573,7 +24579,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// the type parameter was fixed during inference or does not occur at top-level in the return type.
|
||||
const primitiveConstraint = hasPrimitiveConstraint(inference.typeParameter) || isConstTypeVariable(inference.typeParameter);
|
||||
const widenLiteralTypes = !primitiveConstraint && inference.topLevel &&
|
||||
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
|
||||
(inference.isFixed || !isTypeParameterAtTopLevelInReturnType(signature, inference.typeParameter));
|
||||
const baseCandidates = primitiveConstraint ? sameMap(candidates, getRegularTypeOfLiteralType) :
|
||||
widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) :
|
||||
candidates;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
//// [typePredicateTopLevelTypeParameter.ts]
|
||||
// Repro from #51980
|
||||
|
||||
function getPermissions(user: string) {
|
||||
if (user === 'Jack') return 'admin';
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const admins = ['Mike', 'Joe'].map(e => getPermissions(e));
|
||||
|
||||
function isDefined<T>(a: T | undefined): a is T {
|
||||
return a !== undefined;
|
||||
}
|
||||
|
||||
const foundAdmins = admins.filter(isDefined); // "admin"[]
|
||||
|
||||
|
||||
//// [typePredicateTopLevelTypeParameter.js]
|
||||
"use strict";
|
||||
// Repro from #51980
|
||||
function getPermissions(user) {
|
||||
if (user === 'Jack')
|
||||
return 'admin';
|
||||
return undefined;
|
||||
}
|
||||
var admins = ['Mike', 'Joe'].map(function (e) { return getPermissions(e); });
|
||||
function isDefined(a) {
|
||||
return a !== undefined;
|
||||
}
|
||||
var foundAdmins = admins.filter(isDefined); // "admin"[]
|
||||
@@ -0,0 +1,42 @@
|
||||
=== tests/cases/compiler/typePredicateTopLevelTypeParameter.ts ===
|
||||
// Repro from #51980
|
||||
|
||||
function getPermissions(user: string) {
|
||||
>getPermissions : Symbol(getPermissions, Decl(typePredicateTopLevelTypeParameter.ts, 0, 0))
|
||||
>user : Symbol(user, Decl(typePredicateTopLevelTypeParameter.ts, 2, 24))
|
||||
|
||||
if (user === 'Jack') return 'admin';
|
||||
>user : Symbol(user, Decl(typePredicateTopLevelTypeParameter.ts, 2, 24))
|
||||
|
||||
return undefined;
|
||||
>undefined : Symbol(undefined)
|
||||
}
|
||||
|
||||
const admins = ['Mike', 'Joe'].map(e => getPermissions(e));
|
||||
>admins : Symbol(admins, Decl(typePredicateTopLevelTypeParameter.ts, 7, 5))
|
||||
>['Mike', 'Joe'].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>e : Symbol(e, Decl(typePredicateTopLevelTypeParameter.ts, 7, 35))
|
||||
>getPermissions : Symbol(getPermissions, Decl(typePredicateTopLevelTypeParameter.ts, 0, 0))
|
||||
>e : Symbol(e, Decl(typePredicateTopLevelTypeParameter.ts, 7, 35))
|
||||
|
||||
function isDefined<T>(a: T | undefined): a is T {
|
||||
>isDefined : Symbol(isDefined, Decl(typePredicateTopLevelTypeParameter.ts, 7, 59))
|
||||
>T : Symbol(T, Decl(typePredicateTopLevelTypeParameter.ts, 9, 19))
|
||||
>a : Symbol(a, Decl(typePredicateTopLevelTypeParameter.ts, 9, 22))
|
||||
>T : Symbol(T, Decl(typePredicateTopLevelTypeParameter.ts, 9, 19))
|
||||
>a : Symbol(a, Decl(typePredicateTopLevelTypeParameter.ts, 9, 22))
|
||||
>T : Symbol(T, Decl(typePredicateTopLevelTypeParameter.ts, 9, 19))
|
||||
|
||||
return a !== undefined;
|
||||
>a : Symbol(a, Decl(typePredicateTopLevelTypeParameter.ts, 9, 22))
|
||||
>undefined : Symbol(undefined)
|
||||
}
|
||||
|
||||
const foundAdmins = admins.filter(isDefined); // "admin"[]
|
||||
>foundAdmins : Symbol(foundAdmins, Decl(typePredicateTopLevelTypeParameter.ts, 13, 5))
|
||||
>admins.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>admins : Symbol(admins, Decl(typePredicateTopLevelTypeParameter.ts, 7, 5))
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>isDefined : Symbol(isDefined, Decl(typePredicateTopLevelTypeParameter.ts, 7, 59))
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
=== tests/cases/compiler/typePredicateTopLevelTypeParameter.ts ===
|
||||
// Repro from #51980
|
||||
|
||||
function getPermissions(user: string) {
|
||||
>getPermissions : (user: string) => "admin" | undefined
|
||||
>user : string
|
||||
|
||||
if (user === 'Jack') return 'admin';
|
||||
>user === 'Jack' : boolean
|
||||
>user : string
|
||||
>'Jack' : "Jack"
|
||||
>'admin' : "admin"
|
||||
|
||||
return undefined;
|
||||
>undefined : undefined
|
||||
}
|
||||
|
||||
const admins = ['Mike', 'Joe'].map(e => getPermissions(e));
|
||||
>admins : ("admin" | undefined)[]
|
||||
>['Mike', 'Joe'].map(e => getPermissions(e)) : ("admin" | undefined)[]
|
||||
>['Mike', 'Joe'].map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
|
||||
>['Mike', 'Joe'] : string[]
|
||||
>'Mike' : "Mike"
|
||||
>'Joe' : "Joe"
|
||||
>map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
|
||||
>e => getPermissions(e) : (e: string) => "admin" | undefined
|
||||
>e : string
|
||||
>getPermissions(e) : "admin" | undefined
|
||||
>getPermissions : (user: string) => "admin" | undefined
|
||||
>e : string
|
||||
|
||||
function isDefined<T>(a: T | undefined): a is T {
|
||||
>isDefined : <T>(a: T | undefined) => a is T
|
||||
>a : T | undefined
|
||||
|
||||
return a !== undefined;
|
||||
>a !== undefined : boolean
|
||||
>a : T | undefined
|
||||
>undefined : undefined
|
||||
}
|
||||
|
||||
const foundAdmins = admins.filter(isDefined); // "admin"[]
|
||||
>foundAdmins : "admin"[]
|
||||
>admins.filter(isDefined) : "admin"[]
|
||||
>admins.filter : { <S extends "admin" | undefined>(predicate: (value: "admin" | undefined, index: number, array: ("admin" | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "admin" | undefined, index: number, array: ("admin" | undefined)[]) => unknown, thisArg?: any): ("admin" | undefined)[]; }
|
||||
>admins : ("admin" | undefined)[]
|
||||
>filter : { <S extends "admin" | undefined>(predicate: (value: "admin" | undefined, index: number, array: ("admin" | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "admin" | undefined, index: number, array: ("admin" | undefined)[]) => unknown, thisArg?: any): ("admin" | undefined)[]; }
|
||||
>isDefined : <T>(a: T | undefined) => a is T
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// @strict: true
|
||||
|
||||
// Repro from #51980
|
||||
|
||||
function getPermissions(user: string) {
|
||||
if (user === 'Jack') return 'admin';
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const admins = ['Mike', 'Joe'].map(e => getPermissions(e));
|
||||
|
||||
function isDefined<T>(a: T | undefined): a is T {
|
||||
return a !== undefined;
|
||||
}
|
||||
|
||||
const foundAdmins = admins.filter(isDefined); // "admin"[]
|
||||
Reference in New Issue
Block a user