types: fix withDefaults type

This commit is contained in:
三咲智子 Kevin Deng 2023-07-21 00:18:45 +08:00
parent 623ba514ec
commit f703a11a43
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
2 changed files with 47 additions and 22 deletions

View File

@ -19,7 +19,7 @@ describe('defineProps w/ type declaration', () => {
const props = defineProps<{ const props = defineProps<{
foo: string foo: string
bool?: boolean bool?: boolean
boolAndUndefined: boolean | undefined boolOrUndefined: boolean | undefined
}>() }>()
// explicitly declared type should be refined // explicitly declared type should be refined
expectType<string>(props.foo) expectType<string>(props.foo)
@ -27,7 +27,7 @@ describe('defineProps w/ type declaration', () => {
props.bar props.bar
expectType<boolean>(props.bool) expectType<boolean>(props.bool)
expectType<boolean>(props.boolAndUndefined) expectType<boolean>(props.boolOrUndefined)
}) })
describe('defineProps w/ generics', () => { describe('defineProps w/ generics', () => {
@ -52,7 +52,8 @@ describe('defineProps w/ type declaration + withDefaults', () => {
y?: string y?: string
z?: string z?: string
bool?: boolean bool?: boolean
boolAndUndefined: boolean | undefined boolOrUndefined: boolean | undefined
defaultUndefined?: string
}>(), }>(),
{ {
number: 123, number: 123,
@ -61,10 +62,14 @@ describe('defineProps w/ type declaration + withDefaults', () => {
fn: () => {}, fn: () => {},
genStr: () => '', genStr: () => '',
y: undefined, y: undefined,
z: 'string' z: 'string',
defaultUndefined: undefined as string | undefined
} }
) )
// @ts-expect-error
res.number++
res.number + 1 res.number + 1
res.arr.push('hi') res.arr.push('hi')
res.obj.x res.obj.x
@ -75,12 +80,21 @@ describe('defineProps w/ type declaration + withDefaults', () => {
// @ts-expect-error // @ts-expect-error
res.y.slice() res.y.slice()
expectType<string | undefined>(res.x) type T = {
expectType<string | undefined>(res.y) number: number
expectType<string>(res.z) arr: string[]
obj: { x: number }
expectType<boolean>(res.bool) fn: (e: string) => void
expectType<boolean>(res.boolAndUndefined) genStr: string
x: string | undefined
y: string | undefined
z: string
bool: boolean
boolOrUndefined: boolean
defaultUndefined: string | undefined
}
expectType<T>(res)
expectType<typeof res>({} as T)
}) })
describe('defineProps w/ union type declaration + withDefaults', () => { describe('defineProps w/ union type declaration + withDefaults', () => {
@ -127,6 +141,9 @@ describe('defineProps w/ generic type declaration + withDefaults', <T extends
res.n + 1 res.n + 1
// @ts-expect-error readonly
res.n++
expectType<T[] | { x: T }>(res.generic1) expectType<T[] | { x: T }>(res.generic1)
expectType<{ x: T }>(res.generic2) expectType<{ x: T }>(res.generic2)
expectType<TString>(res.generic3) expectType<TString>(res.generic3)

View File

@ -293,23 +293,31 @@ type InferDefault<P, T> =
| ((props: P) => T & {}) | ((props: P) => T & {})
| (T extends NativeType ? T : never) | (T extends NativeType ? T : never)
type NonPartial<T> = {
[K in keyof Required<T>]: T[K]
}
type UndefinedDefault<T, Default> = Default extends undefined
? T
: NotUndefined<T>
type PropsWithDefaults< type PropsWithDefaults<
T, T,
Defaults extends InferDefaults<T>, Defaults extends InferDefaults<T>,
BKeys extends keyof T BKeys extends keyof T
> = Omit<T, keyof Defaults> & { > = Readonly<
[K in keyof Defaults]-?: K extends keyof T NonPartial<Omit<T, keyof (Defaults | BKeys)>> & {
? Defaults[K] extends undefined [K in keyof Defaults]-?: K extends keyof T
? T[K] ? UndefinedDefault<T[K], Defaults[K]>
: NotUndefined<T[K]> : never
: never } & {
} & { [K in BKeys]: K extends keyof Defaults
readonly [K in BKeys]-?: K extends keyof Defaults ? Defaults[K] extends undefined
? Defaults[K] extends undefined ? boolean | undefined
? boolean | undefined : boolean
: boolean : boolean
: boolean }
} >
/** /**
* Vue `<script setup>` compiler macro for providing props default values when * Vue `<script setup>` compiler macro for providing props default values when