Skip to content
Draft
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
15 changes: 13 additions & 2 deletions tsc/internal/checker/emitresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,12 +589,23 @@ func (r *EmitResolver) requiresAddingImplicitUndefined(declaration *ast.Node, sy
}
switch declaration.Kind {
case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindJSDocPropertyTag:
if !isOptionalDeclaration(declaration) {
return false
}
if symbol == nil {
symbol = r.checker.getSymbolOfDeclaration(declaration)
}
isReverseMapped := r.checker.ReverseMappedSymbolLinks.Has(symbol) && r.checker.ReverseMappedSymbolLinks.Get(symbol).mappedType != nil
isMapped := symbol.CheckFlags&ast.CheckFlagsMapped != 0
if symbol.Flags&ast.SymbolFlagsProperty == 0 || !isReverseMapped && !isMapped {
return false
}
t := r.checker.getTypeOfSymbol(symbol)
r.checker.mappedSymbolLinks.Has(symbol)
return (symbol.Flags&ast.SymbolFlagsProperty != 0) && (symbol.Flags&ast.SymbolFlagsOptional != 0) && isOptionalDeclaration(declaration) && r.checker.ReverseMappedSymbolLinks.Has(symbol) && r.checker.ReverseMappedSymbolLinks.Get(symbol).mappedType != nil && containsNonMissingUndefinedType(r.checker, t)
if !containsNonMissingUndefinedType(r.checker, t) {
return false
}
declaredType := declaration.Type()
return declaredType != nil && !r.checker.containsUndefinedType(r.checker.getTypeFromTypeNode(declaredType))
case ast.KindParameter, ast.KindJSDocParameterTag:
return r.requiresAddingImplicitUndefinedWorker(declaration, enclosingDeclaration)
default:
Expand Down
12 changes: 9 additions & 3 deletions tsc/internal/checker/nodebuilderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2246,11 +2246,17 @@ func (b *NodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati
}
}
reportErrors := !b.ctx.suppressReportInferenceFallback
if b.pseudoTypeEquivalentToType(pt, t, !requiresAddingUndefined && (ast.IsParameterDeclaration(declaration) || ast.IsPropertySignatureDeclaration(declaration) || ast.IsPropertyDeclaration(declaration)) && isOptionalDeclaration(declaration), reportErrors) {
isReverseMappedProperty := symbol != nil && b.ch.ReverseMappedSymbolLinks.Has(symbol) && b.ch.ReverseMappedSymbolLinks.Get(symbol).mappedType != nil
isMappedProperty := symbol != nil && (symbol.CheckFlags&ast.CheckFlagsMapped != 0 || isReverseMappedProperty)
// Strada only applies annotation reuse to an existing annotation. For an inferred mapped property,
// validate the pseudo-type with the computed non-missing undefined instead.
addUndefinedForInferredOptional := isMappedProperty && !hasTypeAnnotation(declaration) && (ast.IsPropertySignatureDeclaration(declaration) || ast.IsPropertyDeclaration(declaration)) && isOptionalDeclaration(declaration) && containsNonMissingUndefinedType(b.ch, t)
requiresAddingUndefinedForReuse := requiresAddingUndefined || addUndefinedForInferredOptional
if b.pseudoTypeEquivalentToType(pt, t, !requiresAddingUndefinedForReuse && (ast.IsParameterDeclaration(declaration) || ast.IsPropertySignatureDeclaration(declaration) || ast.IsPropertyDeclaration(declaration)) && isOptionalDeclaration(declaration), reportErrors) {
// !!! TODO: If annotated type node is a reference with insufficient type arguments, we should still fall back to type serialization
// see: canReuseTypeNodeAnnotation in strada for context
ptt := b.pseudoTypeToType(pt)
if ptt != nil && requiresAddingUndefined && containsNonMissingUndefinedType(b.ch, t) && !containsNonMissingUndefinedType(b.ch, ptt) {
if ptt != nil && requiresAddingUndefinedForReuse && containsNonMissingUndefinedType(b.ch, t) && !containsNonMissingUndefinedType(b.ch, ptt) {
pt = pseudochecker.NewPseudoTypeUnion([]*pseudochecker.PseudoType{pt, pseudochecker.PseudoTypeUndefined})
}
result = b.pseudoTypeToNodeWithCheckerFallback(pt, t)
Expand All @@ -2261,7 +2267,7 @@ func (b *NodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati
// pseudoTypeToNodeWithCheckerFallback provides).
reportedInferenceFallback = reportErrors && pt.Kind == pseudochecker.PseudoTypeKindInferred && len(pt.AsPseudoTypeInferred().ErrorNodes) > 0
shouldAddUndefined := false
if requiresAddingUndefined {
if requiresAddingUndefinedForReuse {
if ptt := b.pseudoTypeToType(pt); ptt != nil {
shouldAddUndefined = !containsNonMissingUndefinedType(b.ch, ptt)
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/TypeScript/tsc/internal/fourslash"
"github.com/microsoft/TypeScript/tsc/internal/testutil"
)

func TestQuickInfoMappedPropertyUnionUndefined1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
// @exactOptionalPropertyTypes: true
// https://github.com/microsoft/TypeScript/issues/59948
type OptionalToUnionWithUndefined<T> = {
[K in keyof T]: T extends Record<K, T[K]> ? T[K] : T[K] | undefined;
};
type Intermediate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>;
type Literal/*2*/ = { a?: string | undefined };
type Res1/*3*/ = Required<Intermediate>;
type Res2/*4*/ = Required<Literal>;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "1", "type Intermediate = {\n a?: string | undefined;\n}", "")
f.VerifyQuickInfoAt(t, "2", "type Literal = {\n a?: string | undefined;\n}", "")
f.VerifyQuickInfoAt(t, "3", "type Res1 = {\n a: string | undefined;\n}", "")
f.VerifyQuickInfoAt(t, "4", "type Res2 = {\n a: string | undefined;\n}", "")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/TypeScript/tsc/internal/fourslash"
"github.com/microsoft/TypeScript/tsc/internal/testutil"
)

func TestQuickInfoMappedPropertyUnionUndefined2(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
type OptionalToUnionWithUndefined<T> = {
[K in keyof T]: T extends Record<K, T[K]> ? T[K] : T[K] | undefined;
};
type Intermediate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>;
type Literal/*2*/ = { a?: string | undefined };
type Res1/*3*/ = Required<Intermediate>;
type Res2/*4*/ = Required<Literal>;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "1", "type Intermediate = {\n a?: string | undefined;\n}", "")
f.VerifyQuickInfoAt(t, "2", "type Literal = {\n a?: string | undefined;\n}", "")
f.VerifyQuickInfoAt(t, "3", "type Res1 = {\n a: string;\n}", "")
f.VerifyQuickInfoAt(t, "4", "type Res2 = {\n a: string;\n}", "")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/TypeScript/tsc/internal/fourslash"
"github.com/microsoft/TypeScript/tsc/internal/testutil"
)

func TestQuickInfoMappedPropertyUnionUndefined3(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// https://github.com/microsoft/TypeScript/issues/60411
// @strict: true
type UnsetUndefinedToOblivion<T> = { [P in keyof T]-?: T[P] | undefined };
type SetUndefined<T> = { [P in keyof T]: T[P] | undefined };
type TheWhat/**/ = SetUndefined<UnsetUndefinedToOblivion<{ a?: 1 }>>;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "", "type TheWhat = {\n a: 1 | undefined;\n}", "")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/TypeScript/tsc/internal/fourslash"
"github.com/microsoft/TypeScript/tsc/internal/testutil"
)

func TestQuickInfoMappedPropertyUnionUndefined4(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
type A/*1*/ = { [K in keyof { a?: string }]-?: string };
type B/*2*/ = { [K in keyof A]: string | undefined };`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "1", "type A = {\n a: string;\n}", "")
f.VerifyQuickInfoAt(t, "2", "type B = {\n a: string | undefined;\n}", "")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/TypeScript/tsc/internal/fourslash"
"github.com/microsoft/TypeScript/tsc/internal/testutil"
)

func TestQuickInfoMappedPropertyUnionUndefined5(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
// https://github.com/microsoft/TypeScript/issues/62325
type RequiredKeys<T extends object> = {
[K in keyof Required<T>]: T[K];
};
type Foo = {
a?: string;
b?: number;
c: string;
d: boolean | undefined;
};
type Bar/*1*/ = RequiredKeys<Foo>;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "1", "type Bar = {\n a: string | undefined;\n b: number | undefined;\n c: string;\n d: boolean | undefined;\n}", "")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/TypeScript/tsc/internal/fourslash"
"github.com/microsoft/TypeScript/tsc/internal/testutil"
)

func TestQuickInfoMappedTypeOptionalPropertyExplicitUndefined(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
type X = { x?: number | undefined };
type /*Y*/Y = { [K in keyof X]: X[K] };`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "Y", "type Y = {\n x?: number | undefined;\n}", "")
}

func TestQuickInfoMappedTypeOptionalPropertyExplicitUndefinedExactOptionalPropertyTypes(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
// @exactOptionalPropertyTypes: true
type X = { x?: number | undefined };
type /*Y*/Y = { [K in keyof X]: X[K] };`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "Y", "type Y = {\n x?: number | undefined;\n}", "")
}

func TestQuickInfoMappedTypeRequiredPropertyExplicitUndefinedExactOptionalPropertyTypes(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
// @exactOptionalPropertyTypes: true
type X = { x?: number | undefined };
type /*Y*/Y = { [K in keyof X]-?: X[K] };`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "Y", "type Y = {\n x: number | undefined;\n}", "")
}

func TestQuickInfoMappedTypeOptionalInferredPropertyExplicitUndefined(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
class C { x? = 1 as number }
type M<T> = { [K in keyof T]: T[K] | undefined };
type /*Y*/Y = M<C>;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "Y", "type Y = {\n x?: number | undefined;\n}", "")
}

func TestQuickInfoMappedTypeOptionalInferredPropertyExplicitUndefinedExactOptionalPropertyTypes(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
// @exactOptionalPropertyTypes: true
class C { x? = 1 as number }
type M<T> = { [K in keyof T]: T[K] | undefined };
type /*Y*/Y = M<C>;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "Y", "type Y = {\n x?: number | undefined;\n}", "")
}

func TestQuickInfoMappedTypeOptionalInferredPropertyAliasedUndefined(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
type Maybe<T> = T | undefined;
class C { x? = 1 as number }
type M<T> = { [K in keyof T]: Maybe<T[K]> };
type /*Y*/Y = M<C>;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "Y", "type Y = {\n x?: number | undefined;\n}", "")
}

func TestQuickInfoMappedTypeOptionalInferredPropertyNestedMappedType(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
class C { x? = 1 as number }
type Inner<T> = { [K in keyof T]: T[K] | undefined };
type Outer<T> = { [K in keyof Inner<T>]: Inner<T>[K] };
type /*Y*/Y = Outer<C>;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "Y", "type Y = {\n x?: number | undefined;\n}", "")
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ exports.baddts = foo();

//// [declarationEmitExactOptionalPropertyTypesNodeNotReused.d.ts]
export declare const baddts: (x: {
foo?: string;
foo?: string | undefined;
baz?: undefined;
} & {
bar: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ const foo = <A = {}>() => (x: Out & A) => null
>foo : <A = {}>() => (x: Out & A) => null
><A = {}>() => (x: Out & A) => null : <A = {}>() => (x: Out & A) => null
>(x: Out & A) => null : (x: Out & A) => null
>x : { foo?: string; baz?: undefined; } & { bar: number; } & A
>x : { foo?: string | undefined; baz?: undefined; } & { bar: number; } & A

export const baddts = foo()
>baddts : (x: { foo?: string; baz?: undefined; } & { bar: number; }) => null
>foo() : (x: { foo?: string; baz?: undefined; } & { bar: number; }) => null
>baddts : (x: { foo?: string | undefined; baz?: undefined; } & { bar: number; }) => null
>foo() : (x: { foo?: string | undefined; baz?: undefined; } & { bar: number; }) => null
>foo : <A = {}>() => (x: Out & A) => null

Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ exports.baddts = foo();

//// [declarationEmitExactOptionalPropertyTypesNodeNotReused.d.ts]
export declare const baddts: (x: {
foo?: string;
foo?: string | undefined;
baz?: undefined;
} & {
bar: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ const foo = <A = {}>() => (x: Out & A) => null
>foo : <A = {}>() => (x: Out & A) => null
><A = {}>() => (x: Out & A) => null : <A = {}>() => (x: Out & A) => null
>(x: Out & A) => null : (x: Out & A) => null
>x : { foo?: string; baz?: undefined; } & { bar: number; } & A
>x : { foo?: string | undefined; baz?: undefined; } & { bar: number; } & A

export const baddts = foo()
>baddts : (x: { foo?: string; baz?: undefined; } & { bar: number; }) => null
>foo() : (x: { foo?: string; baz?: undefined; } & { bar: number; }) => null
>baddts : (x: { foo?: string | undefined; baz?: undefined; } & { bar: number; }) => null
>foo() : (x: { foo?: string | undefined; baz?: undefined; } & { bar: number; }) => null
>foo : <A = {}>() => (x: Out & A) => null

Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,11 @@ export { type UseQueryReturnType, useQuery };
=== node_modules/@tanstack/vue-query/build/modern/index.d.ts ===
export { UseQueryReturnType, useQuery } from './useQuery-CPqkvEsh.js';
>UseQueryReturnType : any
>useQuery : <TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends readonly unknown[] = readonly unknown[]>(options: { retry?: (number | boolean | ((failureCount: number, error: TError) => boolean)) | undefined; queryFn?: ((context: { queryKey: TQueryKey; }) => TQueryFnData | Promise<TQueryFnData>) | undefined; queryKey?: TQueryKey | undefined; initialData?: TQueryFnData | undefined; initialDataUpdatedAt?: number | (() => number | undefined); enabled?: boolean; refetchInterval?: number; select?: ((data: TQueryFnData) => TData) | undefined; } & { initialData?: undefined; }) => import("./useQuery-CPqkvEsh.js").UseQueryReturnType<TData, TError>
>useQuery : <TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends readonly unknown[] = readonly unknown[]>(options: { retry?: (number | boolean | ((failureCount: number, error: TError) => boolean)) | undefined; queryFn?: ((context: { queryKey: TQueryKey; }) => TQueryFnData | Promise<TQueryFnData>) | undefined; queryKey?: TQueryKey | undefined; initialData?: TQueryFnData | undefined; initialDataUpdatedAt?: number | (() => number | undefined) | undefined; enabled?: boolean | undefined; refetchInterval?: number | undefined; select?: ((data: TQueryFnData) => TData) | undefined; } & { initialData?: undefined; }) => import("./useQuery-CPqkvEsh.js").UseQueryReturnType<TData, TError>

=== src/index.mts ===
import { useQuery } from '@tanstack/vue-query'
>useQuery : <TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends readonly unknown[] = readonly unknown[]>(options: { retry?: (number | boolean | ((failureCount: number, error: TError) => boolean)) | undefined; queryFn?: ((context: { queryKey: TQueryKey; }) => TQueryFnData | Promise<TQueryFnData>) | undefined; queryKey?: TQueryKey | undefined; initialData?: TQueryFnData | undefined; initialDataUpdatedAt?: number | (() => number | undefined); enabled?: boolean; refetchInterval?: number; select?: ((data: TQueryFnData) => TData) | undefined; } & { initialData?: undefined; }) => import("@tanstack/vue-query").UseQueryReturnType<TData, TError>
>useQuery : <TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends readonly unknown[] = readonly unknown[]>(options: { retry?: (number | boolean | ((failureCount: number, error: TError) => boolean)) | undefined; queryFn?: ((context: { queryKey: TQueryKey; }) => TQueryFnData | Promise<TQueryFnData>) | undefined; queryKey?: TQueryKey | undefined; initialData?: TQueryFnData | undefined; initialDataUpdatedAt?: number | (() => number | undefined) | undefined; enabled?: boolean | undefined; refetchInterval?: number | undefined; select?: ((data: TQueryFnData) => TData) | undefined; } & { initialData?: undefined; }) => import("@tanstack/vue-query").UseQueryReturnType<TData, TError>

const baseUrl = 'https://api.publicapis.org/'
>baseUrl : "https://api.publicapis.org/"
Expand Down Expand Up @@ -412,7 +412,7 @@ export const useEntries = () => {

return useQuery({
>useQuery({ queryKey: entryKeys.list(), queryFn: testApi.getEntries, select: (data) => data.slice(0, 10) }) : import("@tanstack/vue-query").UseQueryReturnType<IEntry[], Error>
>useQuery : <TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends readonly unknown[] = readonly unknown[]>(options: { retry?: (number | boolean | ((failureCount: number, error: TError) => boolean)) | undefined; queryFn?: ((context: { queryKey: TQueryKey; }) => TQueryFnData | Promise<TQueryFnData>) | undefined; queryKey?: TQueryKey | undefined; initialData?: TQueryFnData | undefined; initialDataUpdatedAt?: number | (() => number | undefined); enabled?: boolean; refetchInterval?: number; select?: ((data: TQueryFnData) => TData) | undefined; } & { initialData?: undefined; }) => import("@tanstack/vue-query").UseQueryReturnType<TData, TError>
>useQuery : <TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends readonly unknown[] = readonly unknown[]>(options: { retry?: (number | boolean | ((failureCount: number, error: TError) => boolean)) | undefined; queryFn?: ((context: { queryKey: TQueryKey; }) => TQueryFnData | Promise<TQueryFnData>) | undefined; queryKey?: TQueryKey | undefined; initialData?: TQueryFnData | undefined; initialDataUpdatedAt?: number | (() => number | undefined) | undefined; enabled?: boolean | undefined; refetchInterval?: number | undefined; select?: ((data: TQueryFnData) => TData) | undefined; } & { initialData?: undefined; }) => import("@tanstack/vue-query").UseQueryReturnType<TData, TError>
>{ queryKey: entryKeys.list(), queryFn: testApi.getEntries, select: (data) => data.slice(0, 10) } : { queryKey: readonly ["entries", "list"]; queryFn: () => Promise<IEntry[]>; select: (data: IEntry[]) => IEntry[]; }

queryKey: entryKeys.list(),
Expand Down
Loading