mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-18 07:29:16 -05:00
Narrowing type parameter intersects w/narrowed types
This makes sure that a union type that includes a type parameter is still usable as the actual type that the type guard narrows to.
This commit is contained in:
@@ -7900,13 +7900,17 @@ namespace ts {
|
||||
return TypeFacts.All;
|
||||
}
|
||||
|
||||
function getTypeWithFacts(type: Type, include: TypeFacts) {
|
||||
function getTypeWithFacts(type: Type, include: TypeFacts, intersectForTypeParameters = false) {
|
||||
if (!(type.flags & TypeFlags.Union)) {
|
||||
return getTypeFacts(type) & include ? type : neverType;
|
||||
}
|
||||
let firstType: Type;
|
||||
let hasTypeParameter = false;
|
||||
let types: Type[];
|
||||
for (const t of (type as UnionType).types) {
|
||||
if (t.flags & TypeFlags.TypeParameter) {
|
||||
hasTypeParameter = true;
|
||||
}
|
||||
if (getTypeFacts(t) & include) {
|
||||
if (!firstType) {
|
||||
firstType = t;
|
||||
@@ -7919,7 +7923,19 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
return firstType ? types ? getUnionType(types) : firstType : neverType;
|
||||
const narrowed = types ? getUnionType(types) :
|
||||
firstType ? firstType : neverType;
|
||||
// if there is a type parameter in the narrowed type,
|
||||
// add an intersection with the members of the narrowed type so that the shape of the type is correct
|
||||
if (type.flags & TypeFlags.Union &&
|
||||
narrowed.flags & TypeFlags.Union &&
|
||||
hasTypeParameter &&
|
||||
intersectForTypeParameters) {
|
||||
return getIntersectionType(types.concat([narrowed]));
|
||||
}
|
||||
else {
|
||||
return narrowed;
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeWithDefault(type: Type, defaultExpression: Expression) {
|
||||
@@ -8183,7 +8199,7 @@ namespace ts {
|
||||
let type = getTypeAtFlowNode(flow.antecedent);
|
||||
if (type !== neverType) {
|
||||
// If we have an antecedent type (meaning we're reachable in some way), we first
|
||||
// attempt to narrow the antecedent type. If that produces the nothing type, then
|
||||
// attempt to narrow the antecedent type. If that produces the never type, then
|
||||
// we take the type guard as an indication that control could reach here in a
|
||||
// manner not understood by the control flow analyzer (e.g. a function argument
|
||||
// has an invalid type, or a nested function has possibly made an assignment to a
|
||||
@@ -8292,10 +8308,10 @@ namespace ts {
|
||||
|
||||
function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type {
|
||||
if (isMatchingReference(reference, expr)) {
|
||||
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
|
||||
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy, assumeTrue);
|
||||
}
|
||||
if (isMatchingPropertyAccess(expr)) {
|
||||
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
|
||||
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy, assumeTrue));
|
||||
}
|
||||
return type;
|
||||
}
|
||||
@@ -8353,7 +8369,7 @@ namespace ts {
|
||||
value.kind === SyntaxKind.NullKeyword ?
|
||||
assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull :
|
||||
assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined;
|
||||
return getTypeWithFacts(type, facts);
|
||||
return getTypeWithFacts(type, facts, assumeTrue);
|
||||
}
|
||||
if (type.flags & TypeFlags.NotUnionOrUnit) {
|
||||
return type;
|
||||
@@ -8391,7 +8407,7 @@ namespace ts {
|
||||
const facts = assumeTrue ?
|
||||
getProperty(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject :
|
||||
getProperty(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject;
|
||||
return getTypeWithFacts(type, facts);
|
||||
return getTypeWithFacts(type, facts, assumeTrue);
|
||||
}
|
||||
|
||||
function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
|
||||
|
||||
@@ -35,6 +35,22 @@ function b() {
|
||||
}
|
||||
x; // string
|
||||
}
|
||||
function c<T>(data: string | T): T {
|
||||
if (typeof data === 'string') {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
function d<T extends string>(data: string | T): never {
|
||||
if (typeof data === 'string') {
|
||||
throw new Error('will always happen');
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [controlFlowIfStatement.js]
|
||||
@@ -72,3 +88,19 @@ function b() {
|
||||
}
|
||||
x; // string
|
||||
}
|
||||
function c(data) {
|
||||
if (typeof data === 'string') {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
function d(data) {
|
||||
if (typeof data === 'string') {
|
||||
throw new Error('will always happen');
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,4 +71,42 @@ function b() {
|
||||
x; // string
|
||||
>x : Symbol(x, Decl(controlFlowIfStatement.ts, 26, 7))
|
||||
}
|
||||
function c<T>(data: string | T): T {
|
||||
>c : Symbol(c, Decl(controlFlowIfStatement.ts, 35, 1))
|
||||
>T : Symbol(T, Decl(controlFlowIfStatement.ts, 36, 11))
|
||||
>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14))
|
||||
>T : Symbol(T, Decl(controlFlowIfStatement.ts, 36, 11))
|
||||
>T : Symbol(T, Decl(controlFlowIfStatement.ts, 36, 11))
|
||||
|
||||
if (typeof data === 'string') {
|
||||
>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14))
|
||||
|
||||
return JSON.parse(data);
|
||||
>JSON.parse : Symbol(JSON.parse, Decl(lib.d.ts, --, --))
|
||||
>JSON : Symbol(JSON, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
|
||||
>parse : Symbol(JSON.parse, Decl(lib.d.ts, --, --))
|
||||
>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14))
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14))
|
||||
}
|
||||
}
|
||||
function d<T extends string>(data: string | T): never {
|
||||
>d : Symbol(d, Decl(controlFlowIfStatement.ts, 43, 1))
|
||||
>T : Symbol(T, Decl(controlFlowIfStatement.ts, 44, 11))
|
||||
>data : Symbol(data, Decl(controlFlowIfStatement.ts, 44, 29))
|
||||
>T : Symbol(T, Decl(controlFlowIfStatement.ts, 44, 11))
|
||||
|
||||
if (typeof data === 'string') {
|
||||
>data : Symbol(data, Decl(controlFlowIfStatement.ts, 44, 29))
|
||||
|
||||
throw new Error('will always happen');
|
||||
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
>data : Symbol(data, Decl(controlFlowIfStatement.ts, 44, 29))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,4 +90,51 @@ function b() {
|
||||
x; // string
|
||||
>x : string
|
||||
}
|
||||
function c<T>(data: string | T): T {
|
||||
>c : <T>(data: string | T) => T
|
||||
>T : T
|
||||
>data : string | T
|
||||
>T : T
|
||||
>T : T
|
||||
|
||||
if (typeof data === 'string') {
|
||||
>typeof data === 'string' : boolean
|
||||
>typeof data : string
|
||||
>data : string | T
|
||||
>'string' : "string"
|
||||
|
||||
return JSON.parse(data);
|
||||
>JSON.parse(data) : any
|
||||
>JSON.parse : (text: string, reviver?: (key: any, value: any) => any) => any
|
||||
>JSON : JSON
|
||||
>parse : (text: string, reviver?: (key: any, value: any) => any) => any
|
||||
>data : string & T & (string | T)
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
>data : T
|
||||
}
|
||||
}
|
||||
function d<T extends string>(data: string | T): never {
|
||||
>d : <T extends string>(data: string | T) => never
|
||||
>T : T
|
||||
>data : string | T
|
||||
>T : T
|
||||
|
||||
if (typeof data === 'string') {
|
||||
>typeof data === 'string' : boolean
|
||||
>typeof data : string
|
||||
>data : string | T
|
||||
>'string' : "string"
|
||||
|
||||
throw new Error('will always happen');
|
||||
>new Error('will always happen') : Error
|
||||
>Error : ErrorConstructor
|
||||
>'will always happen' : string
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
>data : never
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,3 +34,19 @@ function b() {
|
||||
}
|
||||
x; // string
|
||||
}
|
||||
function c<T>(data: string | T): T {
|
||||
if (typeof data === 'string') {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
function d<T extends string>(data: string | T): never {
|
||||
if (typeof data === 'string') {
|
||||
throw new Error('will always happen');
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user