Skip to content

types(slots): Add typed slots #2693

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

Closed
wants to merge 12 commits into from
Prev Previous commit
Next Next commit
Merge branch 'main' into feat/typed_slots
# Conflicts:
#	packages/compiler-sfc/src/cssVars.ts
#	packages/dts-test/defineComponent.test-d.tsx
#	packages/runtime-core/src/apiDefineComponent.ts
#	packages/runtime-core/src/component.ts
#	packages/runtime-core/src/componentOptions.ts
#	packages/runtime-core/src/componentPublicInstance.ts
#	packages/runtime-core/src/scheduler.ts
  • Loading branch information
pikax committed Feb 20, 2023
commit 84377365737495994db2b0ac45e67885658f697c
2 changes: 0 additions & 2 deletions packages/compiler-sfc/src/cssVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import { PluginCreator } from 'postcss'
import hash from 'hash-sum'

export const CSS_VARS_HELPER = `useCssVars`
export const cssVarRE =
/\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g

export function genCssVarsFromList(
vars: string[],
Expand Down
257 changes: 163 additions & 94 deletions packages/dts-test/defineComponent.test-d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
SetupContext,
h
} from 'vue'
import { describe, expectType, IsUnion } from './utils'
import {
describe,
expectType,
IsUnion,
} from './utils'

describe('with object props', () => {
interface ExpectedProps {
Expand Down Expand Up @@ -46,98 +50,96 @@ describe('with object props', () => {

type GT = string & { __brand: unknown }

const props = {
a: Number,
// required should make property non-void
b: {
type: String,
required: true as true
},
e: Function,
h: Boolean,
j: Function as PropType<undefined | (() => string | undefined)>,
// default value should infer type and make it non-void
bb: {
default: 'hello'
},
bbb: {
// Note: default function value requires arrow syntax + explicit
// annotation
default: (props: any) => (props.bb as string) || 'foo'
},
bbbb: {
type: String,
default: undefined
},
bbbbb: {
type: String,
default: () => undefined
},
// explicit type casting
cc: Array as PropType<string[]>,
// required + type casting
dd: {
type: Object as PropType<{ n: 1 }>,
required: true as true
},
// return type
ee: Function as PropType<() => string>,
// arguments + object return
ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
// explicit type casting with constructor
ccc: Array as () => string[],
// required + constructor type casting
ddd: {
type: Array as () => string[],
required: true as true
},
// required + object return
eee: {
type: Function as PropType<() => { a: string }>,
required: true as true
},
// required + arguments + object return
fff: {
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
required: true as true
},
hhh: {
type: Boolean,
required: true as true
},
// default + type casting
ggg: {
type: String as PropType<'foo' | 'bar'>,
default: 'foo'
},
// default + function
ffff: {
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
default: (a: number, b: string) => ({ a: a > +b })
},
// union + function with different return types
iii: Function as PropType<(() => string) | (() => number)>,
// union + function with different args & same return type
jjj: {
type: Function as PropType<
((arg1: string) => string) | ((arg1: string, arg2: string) => string)
>,
required: true as true
},
kkk: null,
validated: {
type: String,
// validator requires explicit annotation
validator: (val: unknown) => val !== ''
},
date: Date,
l: [Date],
ll: [Date, Number],
lll: [String, Number]
}

const MyComponent = defineComponent({
props,
props: {
a: Number,
// required should make property non-void
b: {
type: String,
required: true
},
e: Function,
h: Boolean,
j: Function as PropType<undefined | (() => string | undefined)>,
// default value should infer type and make it non-void
bb: {
default: 'hello'
},
bbb: {
// Note: default function value requires arrow syntax + explicit
// annotation
default: (props: any) => (props.bb as string) || 'foo'
},
bbbb: {
type: String,
default: undefined
},
bbbbb: {
type: String,
default: () => undefined
},
// explicit type casting
cc: Array as PropType<string[]>,
// required + type casting
dd: {
type: Object as PropType<{ n: 1 }>,
required: true
},
// return type
ee: Function as PropType<() => string>,
// arguments + object return
ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
// explicit type casting with constructor
ccc: Array as () => string[],
// required + contructor type casting
ddd: {
type: Array as () => string[],
required: true
},
// required + object return
eee: {
type: Function as PropType<() => { a: string }>,
required: true
},
// required + arguments + object return
fff: {
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
required: true
},
hhh: {
type: Boolean,
required: true
},
// default + type casting
ggg: {
type: String as PropType<'foo' | 'bar'>,
default: 'foo'
},
// default + function
ffff: {
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
default: (a: number, b: string) => ({ a: a > +b })
},
// union + function with different return types
iii: Function as PropType<(() => string) | (() => number)>,
// union + function with different args & same return type
jjj: {
type: Function as PropType<
((arg1: string) => string) | ((arg1: string, arg2: string) => string)
>,
required: true
},
kkk: null,
validated: {
type: String,
// validator requires explicit annotation
validator: (val: unknown) => val !== ''
},
date: Date,
l: [Date],
ll: [Date, Number],
lll: [String, Number]
},
setup(props) {
// type assertion. See https://github.com/SamVerschueren/tsd
expectType<ExpectedProps['a']>(props.a)
Expand Down Expand Up @@ -510,9 +512,9 @@ describe('with mixins', () => {
mixins: [MixinA],
data() {
//@ts-expect-error computed are not available on data()
expectError<number>(this.dC1)
expectType<number>(this.dC1)
//@ts-expect-error computed are not available on data()
expectError<string>(this.dC2)
expectType<string>(this.dC2)

return {
d: 4
Expand Down Expand Up @@ -1185,6 +1187,72 @@ describe('async setup', () => {
vm.a = 2
})

// #5948
describe('DefineComponent should infer correct types when assigning to Component', () => {
let component: Component
component = defineComponent({
setup(_, { attrs, slots }) {
// @ts-expect-error should not be any
expectType<[]>(attrs)
// @ts-expect-error should not be any
expectType<[]>(slots)
}
})
expectType<Component>(component)
})

// #5969
describe('should allow to assign props', () => {
const Child = defineComponent({
props: {
bar: String
}
})

const Parent = defineComponent({
props: {
...Child.props,
foo: String
}
})

const child = new Child()
expectType<JSX.Element>(<Parent {...child.$props} />)
})

// #6052
describe('prop starting with `on*` is broken', () => {
defineComponent({
props: {
onX: {
type: Function as PropType<(a: 1) => void>,
required: true
}
},
setup(props) {
expectType<(a: 1) => void>(props.onX)
props.onX(1)
}
})

defineComponent({
props: {
onX: {
type: Function as PropType<(a: 1) => void>,
required: true
}
},
emits: {
test: (a: 1) => true
},
setup(props) {
expectType<(a: 1) => void>(props.onX)
expectType<undefined | ((a: 1) => any)>(props.onTest)
}
})
})


describe('typed slots', () => {
const Comp = defineComponent({
slots: {
Expand Down Expand Up @@ -1300,3 +1368,4 @@ declare const MyButton: DefineComponent<
{}
>
;<MyButton class="x" />

30 changes: 21 additions & 9 deletions packages/runtime-core/src/apiDefineComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ export function defineComponent<
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions,
E extends EmitsOptions = {},
EE extends string = string,
S = any
S = any,
I extends ComponentInjectOptions = {},
II extends string = string
>(
options: ComponentOptionsWithoutProps<
Props,
Expand All @@ -122,7 +124,9 @@ export function defineComponent<
Extends,
E,
EE,
S
S,
I,
II
>
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE, S>

Expand All @@ -137,9 +141,11 @@ export function defineComponent<
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
E extends EmitsOptions = {},
EE extends string = string,
S = any
S = any,
I extends ComponentInjectOptions = {},
II extends string = string
>(
options: ComponentOptionsWithArrayProps<
PropNames,
Expand All @@ -151,7 +157,9 @@ export function defineComponent<
Extends,
E,
EE,
S
S,
I,
II
>
): DefineComponent<
Readonly<{ [key in PropNames]?: any }>,
Expand All @@ -178,9 +186,11 @@ export function defineComponent<
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
E extends EmitsOptions = {},
EE extends string = string,
S = any
S = any,
I extends ComponentInjectOptions = {},
II extends string = string
>(
options: ComponentOptionsWithObjectProps<
PropsOptions,
Expand All @@ -192,7 +202,9 @@ export function defineComponent<
Extends,
E,
EE,
S
S,
I,
II
>
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>

Expand Down
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.