Skip to content

Commit e65e258

Browse files
types(router): add | undefined to optional properties for exactOptionalPropertyTypes consumers
Add | undefined to all optional interface properties across published and internal types. This is a no-op for the default TypeScript configuration but fixes TS2375 errors for consumers with exactOptionalPropertyTypes: true. Zero runtime changes. Zero type assertions. Follows the same approach as vuejs/core#12771 which was merged for @vue/runtime-dom.
1 parent 11f8268 commit e65e258

File tree

19 files changed

+128
-57
lines changed

19 files changed

+128
-57
lines changed

packages/router/__tests__/guards/onBeforeRouteUpdate.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const component = {
2727
function withSpy(name?: string, isAsync = false) {
2828
const spy = vi.fn()
2929
const Component = defineComponent({
30-
name,
30+
name: name || 'no-name',
3131
template: `<p>${name || 'No Name'}</p>`,
3232
setup() {
3333
onBeforeRouteUpdate(spy)

packages/router/__tests__/matcher/resolve.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe('RouterMatcher.resolve', () => {
3737
}
3838

3939
// add location if provided as it should be the same value
40-
if ('path' in location && !('path' in resolved)) {
40+
if ('path' in location && !('path' in resolved) && location.path != null) {
4141
resolved.path = location.path
4242
}
4343

packages/router/__tests__/utils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export interface RouteRecordViewLoose extends Pick<
5757
enterCallbacks: Record<string, Function[]>
5858
props: Record<string, _RouteRecordProps>
5959
aliasOf: RouteRecordNormalized | RouteRecordViewLoose | undefined
60-
children?: RouteRecordRaw[]
60+
children?: RouteRecordRaw[] | undefined
6161
components: Record<string, RouteComponent> | null | undefined
6262
}
6363

@@ -67,17 +67,17 @@ export interface RouteLocationNormalizedLoose extends RouteLocationNormalized {
6767
path: string
6868
// record?
6969
params: any
70-
redirectedFrom?: Partial<MatcherLocation>
70+
redirectedFrom?: Partial<MatcherLocation> | undefined
7171
meta: any
7272
matched: Partial<RouteRecordViewLoose>[]
7373
}
7474

7575
export interface MatcherLocationNormalizedLoose {
76-
name: string
76+
name: string | undefined
7777
path: string
7878
// record?
7979
params: any
80-
redirectedFrom?: Partial<MatcherLocation>
80+
redirectedFrom?: Partial<MatcherLocation> | undefined
8181
meta: any
8282
matched: Partial<RouteRecordViewLoose>[]
8383
instances: Record<string, any>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { describe, it, expectTypeOf } from 'vitest'
2+
import type {
3+
ScrollPositionCoordinates,
4+
_ScrollPositionNormalized,
5+
} from './scrollBehavior'
6+
import type { _RouteRecordBase } from './types'
7+
import type { ResolvedOptions } from './unplugin/options'
8+
9+
describe('exactOptionalPropertyTypes', () => {
10+
it('ScrollPositionCoordinates accepts valid values', () => {
11+
expectTypeOf<ScrollPositionCoordinates>().toEqualTypeOf<{
12+
behavior?: ScrollBehavior
13+
left?: number
14+
top?: number
15+
}>()
16+
})
17+
18+
it('_ScrollPositionNormalized accepts explicit undefined for behavior', () => {
19+
// behavior includes | undefined because savedPosition.behavior
20+
// can be absent at runtime (e.g. computeScrollPosition omits it)
21+
const pos: _ScrollPositionNormalized = {
22+
behavior: undefined,
23+
left: 0,
24+
top: 0,
25+
}
26+
void pos
27+
})
28+
29+
it('_RouteRecordBase meta does not allow undefined', () => {
30+
expectTypeOf<_RouteRecordBase['meta']>().not.toEqualTypeOf<undefined>()
31+
})
32+
33+
it('ResolvedOptions has non-nullable extensions after resolveOptions', () => {
34+
expectTypeOf<ResolvedOptions['extensions']>().toEqualTypeOf<string[]>()
35+
})
36+
37+
it('ResolvedOptions has non-nullable root after resolveOptions', () => {
38+
expectTypeOf<ResolvedOptions['root']>().toEqualTypeOf<string>()
39+
})
40+
41+
it('ResolvedOptions has non-nullable getRouteName after resolveOptions', () => {
42+
expectTypeOf<
43+
ResolvedOptions['getRouteName']
44+
>().not.toEqualTypeOf<undefined>()
45+
})
46+
47+
it('ResolvedOptions has non-nullable dts after resolveOptions', () => {
48+
expectTypeOf<ResolvedOptions['dts']>().not.toEqualTypeOf<undefined>()
49+
})
50+
})

packages/router/src/experimental/data-loaders/auto-exports.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export interface AutoExportLoadersOptions {
5454
* Root of the project. All paths are resolved relatively to this one.
5555
* @default `process.cwd()`
5656
*/
57-
root?: string
57+
root?: string | undefined
5858
}
5959

6060
/**

packages/router/src/experimental/data-loaders/defineColadaLoader.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import type {
4444
import { useRoute, useRouter } from '../../useApi'
4545
import type { Router } from '../../router'
4646
import type { LocationQuery } from '../../query'
47+
import type { RouteParamsGeneric } from '../../types'
4748

4849
/**
4950
* Creates a Pinia Colada data loader with `data` is always defined.
@@ -729,7 +730,7 @@ export interface DataLoaderColadaEntry<
729730

730731
interface TrackedRoute {
731732
ready: boolean
732-
params: Partial<LocationQuery>
733+
params: Partial<RouteParamsGeneric>
733734
query: Partial<LocationQuery>
734735
hash: { v: string | null }
735736
}

packages/router/src/experimental/data-loaders/utils.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { LocationQuery } from '../../query'
21
import { Router } from '../../router'
32
import { RouteLocationNormalizedLoaded } from '../../typed-routes'
43
import type { DataLoaderEntryBase, UseDataLoader } from './createDataLoader'
@@ -113,10 +112,10 @@ function trackObjectReads<T extends Record<string, unknown>>(obj: T) {
113112
* @param outer - the bigger params
114113
* @param inner - the smaller params
115114
*/
116-
export function isSubsetOf(
117-
inner: Partial<LocationQuery>,
118-
outer: LocationQuery
119-
): boolean {
115+
export function isSubsetOf<
116+
V extends string | null | undefined,
117+
T extends Record<string, V | V[]>,
118+
>(inner: Partial<T>, outer: T): boolean {
120119
for (const key in inner) {
121120
const innerValue = inner[key]
122121
const outerValue = outer[key]

packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,13 @@ export class MatcherPatternPathDynamic<
230230
): string {
231231
let paramIndex = 0
232232
let paramName: keyof TParamsOptions
233-
let parser: (TParamsOptions &
234-
Record<
235-
string,
236-
MatcherPatternPathDynamic_ParamOptions<any, any>
237-
>)[keyof TParamsOptions][0]
233+
let parser:
234+
| (TParamsOptions &
235+
Record<
236+
string,
237+
MatcherPatternPathDynamic_ParamOptions<any, any>
238+
>)[keyof TParamsOptions][0]
239+
| undefined
238240
let repeatable: boolean | undefined
239241
let optional: boolean | undefined
240242
let value: ReturnType<NonNullable<ParamParser['set']>> | undefined

packages/router/src/experimental/route-resolver/resolver-fixed.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,22 @@ export interface EXPERIMENTAL_ResolverRecord_Base {
3333
* Name of the matcher. Unique across all matchers. If missing, this record
3434
* cannot be matched. This is useful for grouping records.
3535
*/
36-
name?: RecordName
36+
name?: RecordName | undefined
3737

3838
/**
3939
* {@link MatcherPattern} for the path section of the URI.
4040
*/
41-
path?: MatcherPatternPath
41+
path?: MatcherPatternPath | undefined
4242

4343
/**
4444
* {@link MatcherPattern} for the query section of the URI.
4545
*/
46-
query?: MatcherPatternQuery[]
46+
query?: MatcherPatternQuery[] | undefined
4747

4848
/**
4949
* {@link MatcherPattern} for the hash section of the URI.
5050
*/
51-
hash?: MatcherPatternHash
51+
hash?: MatcherPatternHash | undefined
5252

5353
/**
5454
* Parent record. The parent can be a group or a matchable record.

packages/router/src/location.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export interface LocationNormalized {
2424
*/
2525
interface LocationPartial {
2626
path: string
27-
query?: LocationQueryRaw
28-
hash?: string
27+
query?: LocationQueryRaw | undefined
28+
hash?: string | undefined
2929
}
3030

3131
const TRAILING_SLASH_RE = /\/$/
@@ -212,8 +212,8 @@ export function isSameRouteLocationParams(
212212
}
213213

214214
function isSameRouteLocationParamsValue(
215-
a: RouteParamValue | readonly RouteParamValue[],
216-
b: RouteParamValue | readonly RouteParamValue[]
215+
a: RouteParamValue | readonly RouteParamValue[] | undefined,
216+
b: RouteParamValue | readonly RouteParamValue[] | undefined
217217
): boolean {
218218
return isArray(a)
219219
? isEquivalentArray(a, b)

0 commit comments

Comments
 (0)