Skip to content

Commit 45e3cbb

Browse files
committed
Initial attempt and property witness for in
1 parent d728297 commit 45e3cbb

File tree

3 files changed

+30
-7
lines changed

3 files changed

+30
-7
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,7 @@ namespace ts {
722722
const templateLiteralTypes = new Map<string, TemplateLiteralType>();
723723
const stringMappingTypes = new Map<string, StringMappingType>();
724724
const substitutionTypes = new Map<string, SubstitutionType>();
725+
const propertyWitnessRecordTypes = new Map<string, Type>();
725726
const evolvingArrayTypes: EvolvingArrayType[] = [];
726727
const undefinedProperties: SymbolTable = new Map();
727728

@@ -915,6 +916,7 @@ namespace ts {
915916
let deferredGlobalExtractSymbol: Symbol;
916917
let deferredGlobalOmitSymbol: Symbol;
917918
let deferredGlobalBigIntType: ObjectType;
919+
let deferredGlobalRecordSymbol: Symbol;
918920

919921
const allPotentiallyUnusedIdentifiers = new Map<Path, PotentiallyUnusedIdentifier[]>(); // key is file name
920922

@@ -12658,6 +12660,10 @@ namespace ts {
1265812660
return deferredGlobalBigIntType || (deferredGlobalBigIntType = getGlobalType("BigInt" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
1265912661
}
1266012662

12663+
function getGlobalRecordSymbol(): Symbol {
12664+
return deferredGlobalRecordSymbol || (deferredGlobalRecordSymbol = getGlobalSymbol("Record" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0)!);
12665+
}
12666+
1266112667
/**
1266212668
* Instantiates a global type that is generic with some element type, and returns that instantiation.
1266312669
*/
@@ -22169,7 +22175,20 @@ namespace ts {
2216922175
|| isThisTypeParameter(type)
2217022176
|| type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, t => t.symbol !== globalThisSymbol)) {
2217122177
const propName = escapeLeadingUnderscores(literal.text);
22172-
return filterType(type, t => isTypePresencePossible(t, propName, assumeTrue));
22178+
const narrowed = filterType(type, t => isTypePresencePossible(t, propName, assumeTrue));
22179+
if (assumeTrue && narrowed.flags & TypeFlags.Never) {
22180+
const recordTypeAlias = getGlobalRecordSymbol();
22181+
if (!recordTypeAlias) {
22182+
return errorType;
22183+
}
22184+
let record = propertyWitnessRecordTypes.get(literal.text);
22185+
if (!record) {
22186+
record = getTypeAliasInstantiation(recordTypeAlias, [getLiteralType(literal.text), unknownType]);
22187+
propertyWitnessRecordTypes.set(literal.text, record);
22188+
}
22189+
return getIntersectionType([type, record]);
22190+
}
22191+
return narrowed;
2217322192
}
2217422193
return type;
2217522194
}

tests/baselines/reference/inKeywordTypeguard.errors.txt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ tests/cases/compiler/inKeywordTypeguard.ts(16,11): error TS2339: Property 'a' do
55
tests/cases/compiler/inKeywordTypeguard.ts(27,11): error TS2339: Property 'b' does not exist on type 'AWithOptionalProp | BWithOptionalProp'.
66
Property 'b' does not exist on type 'AWithOptionalProp'.
77
tests/cases/compiler/inKeywordTypeguard.ts(42,11): error TS2339: Property 'b' does not exist on type 'AWithMethod'.
8-
tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type 'never'.
9-
tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type 'never'.
8+
tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type '(AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'.
9+
Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'.
10+
tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type '(AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'.
11+
Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'.
1012
tests/cases/compiler/inKeywordTypeguard.ts(52,11): error TS2339: Property 'a' does not exist on type 'AWithMethod | BWithMethod'.
1113
Property 'a' does not exist on type 'BWithMethod'.
1214
tests/cases/compiler/inKeywordTypeguard.ts(53,11): error TS2339: Property 'b' does not exist on type 'AWithMethod | BWithMethod'.
@@ -85,10 +87,12 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do
8587
if ("c" in x) {
8688
x.a();
8789
~
88-
!!! error TS2339: Property 'a' does not exist on type 'never'.
90+
!!! error TS2339: Property 'a' does not exist on type '(AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'.
91+
!!! error TS2339: Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'.
8992
x.b();
9093
~
91-
!!! error TS2339: Property 'b' does not exist on type 'never'.
94+
!!! error TS2339: Property 'b' does not exist on type '(AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'.
95+
!!! error TS2339: Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'.
9296
} else {
9397
x.a();
9498
~

tests/baselines/reference/inKeywordTypeguard.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,13 @@ function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWit
146146
x.a();
147147
>x.a() : any
148148
>x.a : any
149-
>x : never
149+
>x : (AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)
150150
>a : any
151151

152152
x.b();
153153
>x.b() : any
154154
>x.b : any
155-
>x : never
155+
>x : (AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)
156156
>b : any
157157

158158
} else {

0 commit comments

Comments
 (0)