Skip to content
Open
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
61 changes: 57 additions & 4 deletions tsc/internal/checker/inference.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,28 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type)
case target.flags&TypeFlagsConditional != 0:
c.invokeOnce(n, source, target, (*Checker).inferToConditionalType)
case target.flags&TypeFlagsUnionOrIntersection != 0:
c.inferToMultipleTypes(n, source, target.Types(), target.flags)
c.inferToUnionOrIntersectionType(n, source, target)
case source.flags&TypeFlagsUnion != 0:
// Source is a union or intersection type, infer from each constituent type
// Infer from each source union constituent, excluding incompatible fixed discriminants.
discriminants := c.getInferenceDiscriminants(source, target)
inferSources:
for _, sourceType := range source.Types() {
if len(discriminants) != 0 && sourceType.flags&(TypeFlagsObject|TypeFlagsIntersection) != 0 {
for _, targetProp := range discriminants {
sourceProp := c.getPropertyOfType(sourceType, targetProp.Name)
if sourceProp == nil {
continue
}
// Two optional tags always overlap through absence.
if sourceProp.Flags&ast.SymbolFlagsOptional != 0 && targetProp.Flags&ast.SymbolFlagsOptional != 0 {
continue
}
propType := c.getNonMissingTypeOfSymbol(sourceProp)
if isLiteralType(propType) && !c.isTypeAssignableTo(propType, c.getNonMissingTypeOfSymbol(targetProp)) {
continue inferSources
}
}
}
c.inferFromTypes(n, sourceType, target)
}
case target.flags&TypeFlagsTemplateLiteral != 0:
Expand Down Expand Up @@ -456,7 +474,19 @@ func getTypeListDepth(types []*Type, maxDepth int) int {
return depth
}

func (c *Checker) inferToMultipleTypes(n *InferenceState, source *Type, targets []*Type, targetFlags TypeFlags) {
func (c *Checker) inferToUnionOrIntersectionType(n *InferenceState, source *Type, target *Type) {
var discriminants [][]*ast.Symbol
if target.flags&TypeFlagsUnion != 0 {
sources := source.Distributed()
discriminants = make([][]*ast.Symbol, len(sources))
for i, s := range sources {
discriminants[i] = c.getInferenceDiscriminants(target, s)
}
}
c.inferToMultipleTypes(n, source, target.Types(), target.flags, discriminants)
}

func (c *Checker) inferToMultipleTypes(n *InferenceState, source *Type, targets []*Type, targetFlags TypeFlags, discriminants [][]*ast.Symbol) {
typeVariableCount := 0
if targetFlags&TypeFlagsUnion != 0 {
var nakedTypeVariable *Type
Expand All @@ -477,7 +507,20 @@ func (c *Checker) inferToMultipleTypes(n *InferenceState, source *Type, targets
nakedTypeVariable = t
typeVariableCount++
} else {
inferSources:
for i := range sources {
if len(discriminants) != 0 && len(discriminants[i]) != 0 && t.flags&(TypeFlagsObject|TypeFlagsIntersection) != 0 {
for _, sourceProp := range discriminants[i] {
targetProp := c.getPropertyOfType(t, sourceProp.Name)
if targetProp == nil || sourceProp.Flags&ast.SymbolFlagsOptional != 0 && targetProp.Flags&ast.SymbolFlagsOptional != 0 {
continue
}
propType := c.getNonMissingTypeOfSymbol(targetProp)
if isLiteralType(propType) && !c.isTypeAssignableTo(c.getNonMissingTypeOfSymbol(sourceProp), propType) {
continue inferSources
}
}
}
saveInferencePriority := n.inferencePriority
n.inferencePriority = InferencePriorityMaxValue
c.inferFromTypes(n, sources[i], t)
Expand Down Expand Up @@ -558,7 +601,7 @@ func getSingleTypeVariableFromIntersectionTypes(n *InferenceState, types []*Type
func (c *Checker) inferToMultipleTypesWithPriority(n *InferenceState, source *Type, targets []*Type, targetFlags TypeFlags, newPriority InferencePriority) {
savePriority := n.priority
n.priority |= newPriority
c.inferToMultipleTypes(n, source, targets, targetFlags)
c.inferToMultipleTypes(n, source, targets, targetFlags, nil)
n.priority = savePriority
}

Expand Down Expand Up @@ -1193,6 +1236,16 @@ func (c *Checker) replaceIndexedAccess(instantiable *Type, t *Type, replacement
return c.instantiateType(instantiable, newTypeMapper([]*Type{t.AsIndexedAccessType().indexType, t.AsIndexedAccessType().objectType}, []*Type{c.getNumberLiteralType(0), c.createTupleType([]*Type{replacement})}))
}

func (c *Checker) getInferenceDiscriminants(source *Type, target *Type) []*ast.Symbol {
if target.flags&TypeFlagsObject == 0 {
return nil
}
literalProps := core.Filter(c.getPropertiesOfType(target), func(prop *ast.Symbol) bool {
return isLiteralType(c.getNonMissingTypeOfSymbol(prop))
})
return c.findDiscriminantProperties(literalProps, source)
}

func (c *Checker) typesDefinitelyUnrelated(source *Type, target *Type) bool {
// Two tuple types with incompatible arities are definitely unrelated.
// Two object types that each have a property that is unmatched in the other are definitely unrelated.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ const zero = iterator.filter(isZero);
>isZero : (x: number) => x is 0

const iteratorFromBare = Iterator.from({
>iteratorFromBare : IteratorObject<string, undefined, unknown>
>Iterator.from({ next() { return { done: Math.random() < .5, value: "a string", }; },}) : IteratorObject<string, undefined, unknown>
>iteratorFromBare : IteratorObject<unknown, undefined, unknown>
>Iterator.from({ next() { return { done: Math.random() < .5, value: "a string", }; },}) : IteratorObject<unknown, undefined, unknown>
>Iterator.from : <T>(value: Iterable<T, unknown, undefined> | Iterator<T, unknown, undefined>) => IteratorObject<T, undefined, unknown>
>Iterator : IteratorConstructor
>from : <T>(value: Iterable<T, unknown, undefined> | Iterator<T, unknown, undefined>) => IteratorObject<T, undefined, unknown>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
discriminateWithOptionalProperty2.ts(70,34): error TS2345: Argument of type '{ [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }' is not assignable to parameter of type 'AsyncGenerator<unknown, undefined, undefined> | AsyncIterable<unknown, undefined, undefined>'.
Type '{ [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }' is missing the following properties from type 'AsyncGenerator<unknown, undefined, undefined>': return, throw, [Symbol.asyncDispose]
discriminateWithOptionalProperty2.ts(70,51): error TS18046: 'x' is of type 'unknown'.
discriminateWithOptionalProperty2.ts(70,55): error TS18046: 'x' is of type 'unknown'.


==== discriminateWithOptionalProperty2.ts (3 errors) ====
// https://github.com/microsoft/TypeScript/issues/55532#issuecomment-1694744665

type PromiseOrValue<T> = Promise<T> | T;

function mapAsyncIterable<T, U, R = undefined>(
iterable: AsyncGenerator<T, R, undefined> | AsyncIterable<T, R, undefined>,
callback: (value: T) => PromiseOrValue<U>,
): AsyncGenerator<U, R, void> {
const iterator = iterable[Symbol.asyncIterator]();

async function mapResult(
result: IteratorResult<T, R>,
): Promise<IteratorResult<U, R>> {
if (result.done) {
return result;
}

try {
return { value: await callback(result.value), done: false };
} catch (error) {
if (typeof iterator.return === "function") {
try {
await iterator.return();
} catch (_e) {}
}
throw error;
}
}

return {
async next() {
return mapResult(await iterator.next());
},
async return(value: R): Promise<IteratorResult<U, R>> {
return typeof iterator.return === "function"
? mapResult(await iterator.return(value))
: { value: undefined as any, done: true };
},
async throw(error?: unknown) {
if (typeof iterator.throw === "function") {
return mapResult(await iterator.throw(error));
}
throw error;
},
[Symbol.asyncIterator]() {
return this;
},
async [Symbol.asyncDispose]() {
await this.return(undefined!);
}
};
}

const items = [1, 2, 3];

const iterable = {
[Symbol.asyncIterator]() {
return this;
},
next() {
const value = items[0];
items.shift();
return Promise.resolve({
done: items.length === 0,
value,
});
},
};

const doubles = mapAsyncIterable(iterable, (x) => x + x);
~~~~~~~~
!!! error TS2345: Argument of type '{ [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }' is not assignable to parameter of type 'AsyncGenerator<unknown, undefined, undefined> | AsyncIterable<unknown, undefined, undefined>'.
!!! error TS2345: Type '{ [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }' is missing the following properties from type 'AsyncGenerator<unknown, undefined, undefined>': return, throw, [Symbol.asyncDispose]
~
!!! error TS18046: 'x' is of type 'unknown'.
~
!!! error TS18046: 'x' is of type 'unknown'.

Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,13 @@ const iterable = {
};

const doubles = mapAsyncIterable(iterable, (x) => x + x);
>doubles : AsyncGenerator<number, number, void>
>mapAsyncIterable(iterable, (x) => x + x) : AsyncGenerator<number, number, void>
>doubles : AsyncGenerator<unknown, undefined, void>
>mapAsyncIterable(iterable, (x) => x + x) : AsyncGenerator<unknown, undefined, void>
>mapAsyncIterable : <T, U, R = undefined>(iterable: AsyncGenerator<T, R, undefined> | AsyncIterable<T, R, undefined>, callback: (value: T) => PromiseOrValue<U>) => AsyncGenerator<U, R, void>
>iterable : { [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }
>(x) => x + x : (x: number) => number
>x : number
>x + x : number
>x : number
>x : number
>(x) => x + x : (x: unknown) => any
>x : unknown
>x + x : any
>x : unknown
>x : unknown

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
discriminateWithOptionalProperty2.ts(70,34): error TS2345: Argument of type '{ [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }' is not assignable to parameter of type 'AsyncGenerator<unknown, undefined, undefined> | AsyncIterable<unknown, undefined, undefined>'.
Type '{ [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }' is missing the following properties from type 'AsyncGenerator<unknown, undefined, undefined>': return, throw, [Symbol.asyncDispose]
discriminateWithOptionalProperty2.ts(70,51): error TS18046: 'x' is of type 'unknown'.
discriminateWithOptionalProperty2.ts(70,55): error TS18046: 'x' is of type 'unknown'.


==== discriminateWithOptionalProperty2.ts (3 errors) ====
// https://github.com/microsoft/TypeScript/issues/55532#issuecomment-1694744665

type PromiseOrValue<T> = Promise<T> | T;

function mapAsyncIterable<T, U, R = undefined>(
iterable: AsyncGenerator<T, R, undefined> | AsyncIterable<T, R, undefined>,
callback: (value: T) => PromiseOrValue<U>,
): AsyncGenerator<U, R, void> {
const iterator = iterable[Symbol.asyncIterator]();

async function mapResult(
result: IteratorResult<T, R>,
): Promise<IteratorResult<U, R>> {
if (result.done) {
return result;
}

try {
return { value: await callback(result.value), done: false };
} catch (error) {
if (typeof iterator.return === "function") {
try {
await iterator.return();
} catch (_e) {}
}
throw error;
}
}

return {
async next() {
return mapResult(await iterator.next());
},
async return(value: R): Promise<IteratorResult<U, R>> {
return typeof iterator.return === "function"
? mapResult(await iterator.return(value))
: { value: undefined as any, done: true };
},
async throw(error?: unknown) {
if (typeof iterator.throw === "function") {
return mapResult(await iterator.throw(error));
}
throw error;
},
[Symbol.asyncIterator]() {
return this;
},
async [Symbol.asyncDispose]() {
await this.return(undefined!);
}
};
}

const items = [1, 2, 3];

const iterable = {
[Symbol.asyncIterator]() {
return this;
},
next() {
const value = items[0];
items.shift();
return Promise.resolve({
done: items.length === 0,
value,
});
},
};

const doubles = mapAsyncIterable(iterable, (x) => x + x);
~~~~~~~~
!!! error TS2345: Argument of type '{ [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }' is not assignable to parameter of type 'AsyncGenerator<unknown, undefined, undefined> | AsyncIterable<unknown, undefined, undefined>'.
!!! error TS2345: Type '{ [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }' is missing the following properties from type 'AsyncGenerator<unknown, undefined, undefined>': return, throw, [Symbol.asyncDispose]
~
!!! error TS18046: 'x' is of type 'unknown'.
~
!!! error TS18046: 'x' is of type 'unknown'.

Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,13 @@ const iterable = {
};

const doubles = mapAsyncIterable(iterable, (x) => x + x);
>doubles : AsyncGenerator<number, number, void>
>mapAsyncIterable(iterable, (x) => x + x) : AsyncGenerator<number, number, void>
>doubles : AsyncGenerator<unknown, undefined, void>
>mapAsyncIterable(iterable, (x) => x + x) : AsyncGenerator<unknown, undefined, void>
>mapAsyncIterable : <T, U, R = undefined>(iterable: AsyncGenerator<T, R, undefined> | AsyncIterable<T, R, undefined>, callback: (value: T) => PromiseOrValue<U>) => AsyncGenerator<U, R, void>
>iterable : { [Symbol.asyncIterator](): any; next(): Promise<{ done: boolean; value: number; }>; }
>(x) => x + x : (x: number) => number
>x : number
>x + x : number
>x : number
>x : number
>(x) => x + x : (x: unknown) => any
>x : unknown
>x + x : any
>x : unknown
>x : unknown

Loading