Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 9 additions & 41 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -26200,46 +26200,11 @@ func (c *Checker) getSubstitutionIntersection(t *Type) *Type {
func (c *Checker) shouldDeferIndexType(t *Type, indexFlags IndexFlags) bool {
return t.flags&TypeFlagsInstantiableNonPrimitive != 0 ||
c.isGenericTupleType(t) ||
c.isGenericMappedType(t) && (!c.hasDistributiveNameType(t) || c.getMappedTypeNameTypeKind(t) == MappedTypeNameTypeKindRemapping) ||
c.isGenericMappedType(t) && c.getNameTypeFromMappedType(t) != nil ||
t.flags&TypeFlagsUnion != 0 && indexFlags&IndexFlagsNoReducibleCheck == 0 && c.isGenericReducibleType(t) ||
t.flags&TypeFlagsIntersection != 0 && c.maybeTypeOfKind(t, TypeFlagsInstantiable) && core.Some(t.Types(), c.IsEmptyAnonymousObjectType)
}

// Ordinarily we reduce a keyof M, where M is a mapped type { [P in K as N<P>]: X }, to simply N<K>. This however presumes
// that N distributes over union types, i.e. that N<A | B | C> is equivalent to N<A> | N<B> | N<C>. Specifically, we only
// want to perform the reduction when the name type of a mapped type is distributive with respect to the type variable
// introduced by the 'in' clause of the mapped type. Note that non-generic types are considered to be distributive because
// they're the same type regardless of what's being distributed over.
func (c *Checker) hasDistributiveNameType(mappedType *Type) bool {
typeVariable := c.getTypeParameterFromMappedType(mappedType)
var isDistributive func(*Type) bool
isDistributive = func(t *Type) bool {
switch {
case t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsPrimitive|TypeFlagsNever|TypeFlagsTypeParameter|TypeFlagsObject|TypeFlagsNonPrimitive) != 0:
return true
case t.flags&TypeFlagsConditional != 0:
return t.AsConditionalType().root.isDistributive && t.AsConditionalType().checkType == typeVariable
case t.flags&TypeFlagsUnionOrIntersection != 0:
return core.Every(t.Types(), isDistributive)
case t.flags&TypeFlagsTemplateLiteral != 0:
return core.Every(t.AsTemplateLiteralType().types, isDistributive)
case t.flags&TypeFlagsIndexedAccess != 0:
return isDistributive(t.AsIndexedAccessType().objectType) && isDistributive(t.AsIndexedAccessType().indexType)
case t.flags&TypeFlagsSubstitution != 0:
return isDistributive(t.AsSubstitutionType().baseType) && isDistributive(t.AsSubstitutionType().constraint)
case t.flags&TypeFlagsStringMapping != 0:
return isDistributive(t.Target())
default:
return false
}
}
nameType := c.getNameTypeFromMappedType(mappedType)
if nameType == nil {
nameType = typeVariable
}
return isDistributive(nameType)
}

func (c *Checker) getMappedTypeNameTypeKind(t *Type) MappedTypeNameTypeKind {
nameType := c.getNameTypeFromMappedType(t)
if nameType == nil {
Expand Down Expand Up @@ -26292,7 +26257,7 @@ func (c *Checker) getIndexTypeForMappedType(t *Type, indexFlags IndexFlags) *Typ
// a circular definition. For this reason, we only eagerly manifest the keys if the constraint is non-generic.
if c.isGenericIndexType(constraintType) {
if c.isMappedTypeWithKeyofConstraintDeclaration(t) {
// We have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer
// We have a generic index and a homomorphic mapping and a key remapping - we need to defer
// the whole `keyof whatever` for later since it's not safe to resolve the shape of modifier type.
return c.getIndexTypeForGenericType(t, indexFlags)
}
Expand Down Expand Up @@ -26829,16 +26794,13 @@ func (c *Checker) getBaseConstraintOrType(t *Type) *Type {
}

func (c *Checker) getBaseConstraintOfType(t *Type) *Type {
if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsUnionOrIntersection|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 || c.isGenericTupleType(t) {
if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsUnionOrIntersection|TypeFlagsTemplateLiteral|TypeFlagsStringMapping|TypeFlagsIndex) != 0 || c.isGenericTupleType(t) {
constraint := c.getResolvedBaseConstraint(t, nil)
if constraint != c.noConstraintType && constraint != c.circularConstraintType {
return constraint
}
return nil
}
if t.flags&TypeFlagsIndex != 0 {
return c.stringNumberSymbolType
}
return nil
}

Expand Down Expand Up @@ -26919,6 +26881,12 @@ func (c *Checker) computeBaseConstraint(t *Type, stack []RecursionId) *Type {
}
return nil
case t.flags&TypeFlagsIndex != 0:
if c.isGenericMappedType(t.AsIndexType().target) {
mappedType := t.AsIndexType().target
if c.getNameTypeFromMappedType(mappedType) != nil && !c.isMappedTypeWithKeyofConstraintDeclaration(mappedType) {
return c.getNextBaseConstraint(c.getIndexTypeForMappedType(mappedType, IndexFlagsNone), stack)
}
}
return c.stringNumberSymbolType
case t.flags&TypeFlagsTemplateLiteral != 0:
types := t.Types()
Expand Down
2 changes: 1 addition & 1 deletion internal/checker/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ const (
TypeFlagsInstantiable = TypeFlagsInstantiableNonPrimitive | TypeFlagsInstantiablePrimitive
TypeFlagsStructuredOrInstantiable = TypeFlagsStructuredType | TypeFlagsInstantiable
TypeFlagsObjectFlagsType = TypeFlagsAny | TypeFlagsNullable | TypeFlagsNever | TypeFlagsObject | TypeFlagsUnion | TypeFlagsIntersection
TypeFlagsSimplifiable = TypeFlagsIndexedAccess | TypeFlagsConditional
TypeFlagsSimplifiable = TypeFlagsIndexedAccess | TypeFlagsConditional | TypeFlagsIndex
TypeFlagsSingleton = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsString | TypeFlagsNumber | TypeFlagsBoolean | TypeFlagsBigInt | TypeFlagsESSymbol | TypeFlagsVoid | TypeFlagsUndefined | TypeFlagsNull | TypeFlagsNever | TypeFlagsNonPrimitive
// 'TypeFlagsNarrowable' types are types where narrowing actually narrows.
// This *should* be every type other than null, undefined, void, and never
Expand Down
3 changes: 0 additions & 3 deletions internal/testrunner/compiler_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,6 @@ func (r *CompilerBaselineRunner) EnumerateTestFiles() []string {
}

var skippedTests = []string{
// Broken until further porting work is done
"mappedTypeAsClauseRecursiveNoCrash1.ts",

// Flaky
"for-of29.ts",

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//// [tests/cases/conformance/types/mapped/mappedTypeAsClauseRecursiveNoCrash1.ts] ////

=== mappedTypeAsClauseRecursiveNoCrash1.ts ===
// https://github.com/microsoft/TypeScript/issues/60476

export type FlattenType<Source extends object, Target> = {
>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 24))
>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 46))

[Key in keyof Source as Key extends string
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))
>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 24))
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))

? Source[Key] extends object
>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 24))
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))

? `${Key}.${keyof FlattenType<Source[Key], Target> & string}`
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))
>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 24))
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))
>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 46))

: Key
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))

: never]-?: Target;
>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 46))

};

type FieldSelect = {
>FieldSelect : Symbol(FieldSelect, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 8, 2))

table: string;
>table : Symbol(table, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 10, 20))

field: string;
>field : Symbol(field, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 11, 16))

};

type Address = {
>Address : Symbol(Address, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 13, 2))

postCode: string;
>postCode : Symbol(postCode, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 15, 16))

description: string;
>description : Symbol(description, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 16, 19))

address: string;
>address : Symbol(address, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 17, 22))

};

type User = {
>User : Symbol(User, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 19, 2))

id: number;
>id : Symbol(id, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 21, 13))

name: string;
>name : Symbol(name, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 22, 13))

address: Address;
>address : Symbol(address, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 23, 15))
>Address : Symbol(Address, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 13, 2))

};

type FlattenedUser = FlattenType<User, FieldSelect>;
>FlattenedUser : Symbol(FlattenedUser, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 25, 2))
>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
>User : Symbol(User, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 19, 2))
>FieldSelect : Symbol(FieldSelect, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 8, 2))

type FlattenedUserKeys = keyof FlattenType<User, FieldSelect>;
>FlattenedUserKeys : Symbol(FlattenedUserKeys, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 27, 52))
>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
>User : Symbol(User, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 19, 2))
>FieldSelect : Symbol(FieldSelect, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 8, 2))

export type FlattenTypeKeys<Source extends object, Target> = keyof {
>FlattenTypeKeys : Symbol(FlattenTypeKeys, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 28, 62))
>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 28))
>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 50))

[Key in keyof Source as Key extends string
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))
>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 28))
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))

? Source[Key] extends object
>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 28))
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))

? `${Key}.${keyof FlattenType<Source[Key], Target> & string}`
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))
>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 28))
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))
>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 50))

: Key
>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))

: never]-?: Target;
>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 50))

};

type FlattenedUserKeys2 = FlattenTypeKeys<User, FieldSelect>;
>FlattenedUserKeys2 : Symbol(FlattenedUserKeys2, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 36, 2))
>FlattenTypeKeys : Symbol(FlattenTypeKeys, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 28, 62))
>User : Symbol(User, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 19, 2))
>FieldSelect : Symbol(FieldSelect, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 8, 2))

Loading
Loading