)
})
+describe('defineProps w/ generic type declaration + withDefaults', <T extends number, TA extends {
+ a: string
+}, TString extends string>() => {
+ const res = withDefaults(
+ defineProps<{
+ n?: number
+ bool?: boolean
+
+ generic1?: T[] | { x: T }
+ generic2?: { x: T }
+ generic3?: TString
+ generic4?: TA
+ }>(),
+ {
+ n: 123,
+
+ generic1: () => [123, 33] as T[],
+ generic2: () => ({ x: 123 } as { x: T }),
+
+ generic3: () => 'test' as TString,
+ generic4: () => ({ a: 'test' } as TA)
+ }
+ )
+
+ res.n + 1
+
+ expectType<T[] | { x: T }>(res.generic1)
+ expectType<{ x: T }>(res.generic2)
+ expectType<TString>(res.generic3)
+ expectType<TA>(res.generic4)
+
+ expectType<boolean>(res.bool)
+})
+
describe('defineProps w/ runtime declaration', () => {
// runtime declaration
const props = defineProps({
PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions
>(props: PP): Prettify<Readonly<ExtractPropTypes<PP>>>
// overload 3: typed-based declaration
-export function defineProps<TypeProps>(): DefineProps<TypeProps>
+export function defineProps<TypeProps>(): DefineProps<
+ TypeProps,
+ BooleanKey<TypeProps>
+>
// implementation
export function defineProps() {
if (__DEV__) {
return null as any
}
-type DefineProps<T> = Readonly<T> & {
- readonly [K in BooleanKey<T>]-?: boolean
+type DefineProps<T, BKeys extends keyof T> = Readonly<T> & {
+ readonly [K in BKeys]-?: boolean
}
type BooleanKey<T, K extends keyof T = keyof T> = K extends any
type NotUndefined<T> = T extends undefined ? never : T
type InferDefaults<T> = {
- [K in keyof T]?: InferDefault<T, NotUndefined<T[K]>>
+ [K in keyof T]?: InferDefault<T, T[K]>
}
-type InferDefault<P, T> = T extends
- | null
- | number
- | string
- | boolean
- | symbol
- | Function
- ? T | ((props: P) => T)
- : (props: P) => T
-
-type PropsWithDefaults<Base, Defaults> = Base & {
- [K in keyof Defaults]: K extends keyof Base
+type NativeType = null | number | string | boolean | symbol | Function
+
+type InferDefault<P, T> =
+ | ((props: P) => T & {})
+ | (T extends NativeType ? T : never)
+
+type PropsWithDefaults<
+ T,
+ Defaults extends InferDefaults<T>,
+ BKeys extends keyof T
+> = Omit<T, keyof Defaults> & {
+ [K in keyof Defaults]-?: K extends keyof T
? Defaults[K] extends undefined
- ? Base[K]
- : NotUndefined<Base[K]>
+ ? T[K]
+ : NotUndefined<T[K]>
: never
-}
+} & { readonly [K in BKeys]-?: boolean }
+
/**
* Vue `<script setup>` compiler macro for providing props default values when
* using type-based `defineProps` declaration.
*
* @see {@link https://vuejs.org/guide/typescript/composition-api.html#typing-component-props}
*/
-export function withDefaults<Props, Defaults extends InferDefaults<Props>>(
- props: Props,
+export function withDefaults<
+ T,
+ BKeys extends keyof T,
+ Defaults extends InferDefaults<T>
+>(
+ props: DefineProps<T, BKeys>,
defaults: Defaults
-): PropsWithDefaults<Props, Defaults> {
+): PropsWithDefaults<T, Defaults, BKeys> {
if (__DEV__) {
warnRuntimeUsage(`withDefaults`)
}