Skip to content

Commit 58594d6

Browse files
committed
feat(types): infer attrs in defineComponent
1 parent a0e7dc3 commit 58594d6

File tree

6 files changed

+194
-39
lines changed

6 files changed

+194
-39
lines changed

packages/dts-test/defineComponent.test-d.tsx

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,7 +1040,7 @@ describe('inject', () => {
10401040
expectType<unknown>(this.foo)
10411041
expectType<unknown>(this.bar)
10421042
// @ts-expect-error
1043-
this.foobar = 1
1043+
expectError((this.foobar = 1))
10441044
}
10451045
})
10461046

@@ -1052,7 +1052,7 @@ describe('inject', () => {
10521052
expectType<unknown>(this.foo)
10531053
expectType<unknown>(this.bar)
10541054
// @ts-expect-error
1055-
this.foobar = 1
1055+
expectError((this.foobar = 1))
10561056
}
10571057
})
10581058

@@ -1072,7 +1072,7 @@ describe('inject', () => {
10721072
expectType<unknown>(this.foo)
10731073
expectType<unknown>(this.bar)
10741074
// @ts-expect-error
1075-
this.foobar = 1
1075+
expectError((this.foobar = 1))
10761076
}
10771077
})
10781078

@@ -1081,9 +1081,9 @@ describe('inject', () => {
10811081
props: ['a', 'b'],
10821082
created() {
10831083
// @ts-expect-error
1084-
this.foo = 1
1084+
expectError((this.foo = 1))
10851085
// @ts-expect-error
1086-
this.bar = 1
1086+
expectError((this.bar = 1))
10871087
}
10881088
})
10891089
})
@@ -1185,6 +1185,118 @@ describe('async setup', () => {
11851185
vm.a = 2
11861186
})
11871187

1188+
describe('define attrs', () => {
1189+
test('define attrs w/ object props', () => {
1190+
type CompAttrs = {
1191+
bar: number
1192+
baz?: string
1193+
}
1194+
const MyComp = defineComponent(
1195+
{
1196+
props: {
1197+
foo: String
1198+
},
1199+
created() {
1200+
expectType<CompAttrs['bar']>(this.$attrs.bar)
1201+
expectType<CompAttrs['baz']>(this.$attrs.baz)
1202+
}
1203+
},
1204+
{
1205+
attrs: {} as CompAttrs
1206+
}
1207+
)
1208+
expectType<JSX.Element>(<MyComp foo="1" bar={1} />)
1209+
})
1210+
1211+
test('define attrs w/ array props', () => {
1212+
type CompAttrs = {
1213+
bar: number
1214+
baz?: string
1215+
}
1216+
const MyComp = defineComponent(
1217+
{
1218+
props: ['foo'],
1219+
created() {
1220+
expectType<CompAttrs['bar']>(this.$attrs.bar)
1221+
expectType<CompAttrs['baz']>(this.$attrs.baz)
1222+
}
1223+
},
1224+
{
1225+
attrs: {} as CompAttrs
1226+
}
1227+
)
1228+
expectType<JSX.Element>(<MyComp foo="1" bar={1} />)
1229+
})
1230+
1231+
test('define attrs w/ no props', () => {
1232+
type CompAttrs = {
1233+
bar: number
1234+
baz?: string
1235+
}
1236+
const MyComp = defineComponent(
1237+
{
1238+
created() {
1239+
expectType<CompAttrs['bar']>(this.$attrs.bar)
1240+
expectType<CompAttrs['baz']>(this.$attrs.baz)
1241+
}
1242+
},
1243+
{
1244+
attrs: {} as CompAttrs
1245+
}
1246+
)
1247+
expectType<JSX.Element>(<MyComp bar={1} />)
1248+
})
1249+
1250+
test('define attrs w/ function component', () => {
1251+
type CompAttrs = {
1252+
bar: number
1253+
baz?: string
1254+
}
1255+
const MyComp = defineComponent(
1256+
(_props: { foo: string }, ctx: SetupContext<{}, CompAttrs>) => {
1257+
expectType<number>(ctx.attrs.bar)
1258+
expectType<CompAttrs['bar']>(ctx.attrs.bar)
1259+
expectType<CompAttrs['baz']>(ctx.attrs.baz)
1260+
}
1261+
)
1262+
expectType<JSX.Element>(<MyComp foo={'1'} bar={1} />)
1263+
})
1264+
1265+
test('define attrs as low priority', () => {
1266+
type CompAttrs = {
1267+
foo: number
1268+
}
1269+
const MyComp = defineComponent(
1270+
{
1271+
props: {
1272+
foo: String
1273+
},
1274+
created() {
1275+
// @ts-expect-error
1276+
console.log(this.$attrs.foo)
1277+
}
1278+
},
1279+
{
1280+
attrs: {} as CompAttrs
1281+
}
1282+
)
1283+
expectType<JSX.Element>(<MyComp foo="1" />)
1284+
})
1285+
1286+
test('define attrs w/ default attrs such as class, style', () => {
1287+
const MyComp = defineComponent({
1288+
props: {
1289+
foo: String
1290+
},
1291+
created() {
1292+
expectType<unknown>(this.$attrs.class)
1293+
expectType<unknown>(this.$attrs.style)
1294+
}
1295+
})
1296+
expectType<JSX.Element>(<MyComp class="1" style={1} />)
1297+
})
1298+
})
1299+
11881300
// #5948
11891301
describe('DefineComponent should infer correct types when assigning to Component', () => {
11901302
let component: Component

packages/runtime-core/src/apiDefineComponent.ts

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export type DefineComponent<
4141
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
4242
E extends EmitsOptions = {},
4343
EE extends string = string,
44+
Attrs = {},
4445
PP = PublicProps,
4546
Props = Readonly<
4647
PropsOrPropOptions extends ComponentPropsOptions
@@ -61,7 +62,9 @@ export type DefineComponent<
6162
E,
6263
PP & Props,
6364
Defaults,
64-
true
65+
true,
66+
{},
67+
Attrs
6568
> &
6669
Props
6770
> &
@@ -86,12 +89,12 @@ export type DefineComponent<
8689

8790
// overload 1: direct setup function
8891
// (uses user defined props interface)
89-
export function defineComponent<Props, RawBindings = object>(
92+
export function defineComponent<Props, RawBindings = object, Attrs = {}>(
9093
setup: (
9194
props: Readonly<Props>,
92-
ctx: SetupContext
95+
ctx: SetupContext<{}, Attrs>
9396
) => RawBindings | RenderFunction
94-
): DefineComponent<Props, RawBindings>
97+
): DefineComponent<Props, RawBindings, {}, {}, {}, {}, {}, {}, string, Attrs>
9598

9699
// overload 2: object format with no props
97100
// (uses user defined props interface)
@@ -107,9 +110,10 @@ export function defineComponent<
107110
E extends EmitsOptions = {},
108111
EE extends string = string,
109112
I extends ComponentInjectOptions = {},
110-
II extends string = string
113+
II extends string = string,
114+
Attrs = {}
111115
>(
112-
options: ComponentOptionsWithoutProps<
116+
comp: ComponentOptionsWithoutProps<
113117
Props,
114118
RawBindings,
115119
D,
@@ -120,9 +124,13 @@ export function defineComponent<
120124
E,
121125
EE,
122126
I,
123-
II
124-
>
125-
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>
127+
II,
128+
Attrs
129+
>,
130+
options?: {
131+
attrs: Attrs
132+
}
133+
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE, Attrs>
126134

127135
// overload 3: object format with array props declaration
128136
// props inferred as { [key in PropNames]?: any }
@@ -138,9 +146,10 @@ export function defineComponent<
138146
E extends EmitsOptions = {},
139147
EE extends string = string,
140148
I extends ComponentInjectOptions = {},
141-
II extends string = string
149+
II extends string = string,
150+
Attrs = {}
142151
>(
143-
options: ComponentOptionsWithArrayProps<
152+
comp: ComponentOptionsWithArrayProps<
144153
PropNames,
145154
RawBindings,
146155
D,
@@ -151,8 +160,12 @@ export function defineComponent<
151160
E,
152161
EE,
153162
I,
154-
II
155-
>
163+
II,
164+
Attrs
165+
>,
166+
options?: {
167+
attrs: Attrs
168+
}
156169
): DefineComponent<
157170
Readonly<{ [key in PropNames]?: any }>,
158171
RawBindings,
@@ -162,7 +175,8 @@ export function defineComponent<
162175
Mixin,
163176
Extends,
164177
E,
165-
EE
178+
EE,
179+
Attrs
166180
>
167181

168182
// overload 4: object format with object props declaration
@@ -180,9 +194,10 @@ export function defineComponent<
180194
E extends EmitsOptions = {},
181195
EE extends string = string,
182196
I extends ComponentInjectOptions = {},
183-
II extends string = string
197+
II extends string = string,
198+
Attrs = {}
184199
>(
185-
options: ComponentOptionsWithObjectProps<
200+
comp: ComponentOptionsWithObjectProps<
186201
PropsOptions,
187202
RawBindings,
188203
D,
@@ -193,11 +208,26 @@ export function defineComponent<
193208
E,
194209
EE,
195210
I,
196-
II
197-
>
198-
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
211+
II,
212+
Attrs
213+
>,
214+
options?: {
215+
attrs: Attrs
216+
}
217+
): DefineComponent<
218+
PropsOptions,
219+
RawBindings,
220+
D,
221+
C,
222+
M,
223+
Mixin,
224+
Extends,
225+
E,
226+
EE,
227+
Attrs
228+
>
199229

200230
// implementation, close to no-op
201-
export function defineComponent(options: unknown) {
202-
return isFunction(options) ? { setup: options, name: options.name } : options
231+
export function defineComponent(comp: unknown, options?: unknown) {
232+
return isFunction(comp) ? { setup: comp, name: comp.name } : comp
203233
}

packages/runtime-core/src/apiSetupHelpers.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
setCurrentInstance,
55
SetupContext,
66
createSetupContext,
7-
unsetCurrentInstance
7+
unsetCurrentInstance,
8+
Data
89
} from './component'
910
import { EmitFn, EmitsOptions } from './componentEmits'
1011
import {
@@ -196,8 +197,8 @@ export function useSlots(): SetupContext['slots'] {
196197
return getContext().slots
197198
}
198199

199-
export function useAttrs(): SetupContext['attrs'] {
200-
return getContext().attrs
200+
export function useAttrs<T extends Data = {}>(): SetupContext['attrs'] {
201+
return getContext().attrs as T
201202
}
202203

203204
function getContext(): SetupContext {

packages/runtime-core/src/component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,9 @@ export type { ComponentOptions }
167167
type LifecycleHook<TFn = Function> = TFn[] | null
168168

169169
// use `E extends any` to force evaluating type to fix #2362
170-
export type SetupContext<E = EmitsOptions> = E extends any
170+
export type SetupContext<E = EmitsOptions, Attrs = Data> = E extends any
171171
? {
172-
attrs: Data
172+
attrs: Attrs
173173
slots: Slots
174174
emit: EmitFn<E>
175175
expose: (exposed?: Record<string, any>) => void

packages/runtime-core/src/componentOptions.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ export type ComponentOptionsWithoutProps<
229229
EE extends string = string,
230230
I extends ComponentInjectOptions = {},
231231
II extends string = string,
232+
Attrs = {},
232233
PE = Props & EmitsToProps<E>
233234
> = ComponentOptionsBase<
234235
PE,
@@ -258,7 +259,8 @@ export type ComponentOptionsWithoutProps<
258259
PE,
259260
{},
260261
false,
261-
I
262+
I,
263+
Attrs
262264
>
263265
>
264266

@@ -274,6 +276,7 @@ export type ComponentOptionsWithArrayProps<
274276
EE extends string = string,
275277
I extends ComponentInjectOptions = {},
276278
II extends string = string,
279+
Attrs = {},
277280
Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps<E>
278281
> = ComponentOptionsBase<
279282
Props,
@@ -303,7 +306,8 @@ export type ComponentOptionsWithArrayProps<
303306
Props,
304307
{},
305308
false,
306-
I
309+
I,
310+
Attrs
307311
>
308312
>
309313

@@ -319,6 +323,7 @@ export type ComponentOptionsWithObjectProps<
319323
EE extends string = string,
320324
I extends ComponentInjectOptions = {},
321325
II extends string = string,
326+
Attrs = {},
322327
Props = Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
323328
Defaults = ExtractDefaultPropTypes<PropsOptions>
324329
> = ComponentOptionsBase<
@@ -349,7 +354,8 @@ export type ComponentOptionsWithObjectProps<
349354
Props,
350355
Defaults,
351356
false,
352-
I
357+
I,
358+
Attrs
353359
>
354360
>
355361

0 commit comments

Comments
 (0)