Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generated route param types should include param matchers guard checks #8137

Closed
minht11 opened this issue Dec 13, 2022 · 4 comments · Fixed by #10755
Closed

Generated route param types should include param matchers guard checks #8137

minht11 opened this issue Dec 13, 2022 · 4 comments · Fixed by #10755
Labels
contributions-welcome We're not going to implement this ourselves, but we'll consider contributions feature / enhancement New feature or request types / typescript
Milestone

Comments

@minht11
Copy link

minht11 commented Dec 13, 2022

Describe the problem

Typed route param types do not match ones provided from params/* match functions.

Consider following setup

// params/fruit.ts

import type { ParamMatcher } from '@sveltejs/kit'

const fruits = ['apple', 'banana', 'cherry'] as const
type Fruit = typeof fruits[number]

export const match = ((param): param is Fruit =>
	fruits.includes(param as Fruit)) satisfies ParamMatcher

Match function is strictly typed using type guard, so typescript knows that function only returns true when param is a fruit.
Now trying to access it inside page using it gives you incorrect type

// routes/[slug=fruit]/+page.svelte
<script lang="ts">
	import { page } from '$app/stores'

	let slug = $page.params.slug // This will be typed as 'string' | 'undefined'
</script>

<h1>Fruit</h1>
<div>{slug}</div>

Describe the proposed solution

Generated route $types.d.ts should take into account match function guard checks, so instead of generated output of

type RouteParams = { slug: string }

it should be

type RouteParams = { slug: 'apple' | 'banana' | 'cherry' }

if using guard types it could even exclude undefined from final type since it would never match it too.

Alternatives considered

No response

Importance

nice to have

Additional Information

No response

@Rich-Harris
Copy link
Member

I had the same thought the other day — this would be a cool addition

@dummdidumm
Copy link
Member

We can probably infer something for the load functions, but for the page store we can't because we can't scope-type it. You would need to do that manually, or rather we would need to provide a App.Params interface which you can type yourself in app.d.ts

@Rich-Harris Rich-Harris added the contributions-welcome We're not going to implement this ourselves, but we'll consider contributions label Jan 31, 2023
@Rich-Harris Rich-Harris added this to the later milestone Jan 31, 2023
@olehmisar
Copy link

@dummdidumm what if page was imported from something like ./$stores? Would that work?

import { page } from "./$stores";
$page.params.slug // string

@LorisSigrist
Copy link
Contributor

We can infer which values would pass the match function using the following generic:

const fruits = ['apple', 'banana', 'cherry'] as const
type Fruit = typeof fruits[number]

export const match = (param : string) : param is Fruit => fruits.includes(param as Fruit)

//If there is a typeguard specified, use that
//otherwise fallback to string
type ValidParams<T> = T extends (param: string) => param is (infer U) ? U : string;

// ValidParams<typeof match> = 'apple' | 'banana' | 'cherry' - Gets inferred from typeguard

I actually get a red squiggly on the infer U bit in my IDE, but it still works so 🙃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contributions-welcome We're not going to implement this ourselves, but we'll consider contributions feature / enhancement New feature or request types / typescript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants