diff --git a/packages/dts-test/componentTypeExtensions.test-d.tsx b/packages/dts-test/componentTypeExtensions.test-d.tsx index 68364191945..fe58c0a6449 100644 --- a/packages/dts-test/componentTypeExtensions.test-d.tsx +++ b/packages/dts-test/componentTypeExtensions.test-d.tsx @@ -1,4 +1,4 @@ -import { defineComponent } from 'vue' +import { type DefineComponent, type Directive, defineComponent } from 'vue' import { expectType } from './utils' declare module 'vue' { @@ -6,6 +6,14 @@ declare module 'vue' { test?(n: number): void } + interface GlobalDirectives { + test: Directive + } + + interface GlobalComponents { + RouterView: DefineComponent<{}> + } + interface ComponentCustomProperties { state?: 'stopped' | 'running' } @@ -46,6 +54,8 @@ export const Custom = defineComponent({ }, }) +expectType(Custom.directives!.test) +expectType>(Custom.components!.RouterView) expectType() expectType() expectType() diff --git a/packages/dts-test/defineComponent.test-d.tsx b/packages/dts-test/defineComponent.test-d.tsx index 41646751b8b..44a00d4e586 100644 --- a/packages/dts-test/defineComponent.test-d.tsx +++ b/packages/dts-test/defineComponent.test-d.tsx @@ -1501,18 +1501,108 @@ describe('should work when props type is incompatible with setup returned type ' describe('withKeys and withModifiers as pro', () => { const onKeydown = withKeys(e => {}, ['']) + // @ts-expect-error invalid modifiers const onClick = withModifiers(e => {}, ['']) ; }) +// #3367 expose components types +describe('expose component types', () => { + const child = defineComponent({ + props: { + a: String, + }, + }) + + const parent = defineComponent({ + components: { + child, + child2: { + template: `
`, + }, + }, + }) + + expectType(parent.components!.child) + expectType(parent.components!.child2) + + // global components + expectType>( + new parent.components!.KeepAlive().$props, + ) + expectType>(new child.components!.KeepAlive().$props) + + // runtime-dom components + expectType>( + new parent.components!.Transition().$props, + ) + expectType>( + new child.components!.Transition().$props, + ) +}) + +describe('directive typing', () => { + const customDirective: Directive = { + created(_) {}, + } + + const comp = defineComponent({ + props: { + a: String, + }, + directives: { + customDirective, + localDirective: { + created(_, { arg }) { + expectType(arg) + }, + }, + }, + }) + + expectType(comp.directives!.customDirective) + expectType(comp.directives!.localDirective) + + // global directive + expectType(comp.directives!.vShow) +}) + +describe('expose typing', () => { + const Comp = defineComponent({ + expose: ['a', 'b'], + props: { + some: String, + }, + data() { + return { a: 1, b: '2', c: 1 } + }, + }) + + expectType>(Comp.expose!) + + const vm = new Comp() + // internal should still be exposed + vm.$props + + expectType(vm.a) + expectType(vm.b) + + // @ts-expect-error shouldn't be exposed + vm.c +}) + import type { AllowedComponentProps, ComponentCustomProps, ComponentOptionsMixin, DefineComponent, + Directive, EmitsOptions, ExtractPropTypes, + KeepAliveProps, + TransitionProps, VNodeProps, + vShow, } from 'vue' // code generated by tsc / vue-tsc, make sure this continues to work diff --git a/packages/dts-test/directives.test-d.ts b/packages/dts-test/directives.test-d.ts new file mode 100644 index 00000000000..5b87ebf71e5 --- /dev/null +++ b/packages/dts-test/directives.test-d.ts @@ -0,0 +1,58 @@ +import { type Directive, type ObjectDirective, vModelText } from 'vue' +import { describe, expectType } from './utils' + +type ExtractBinding = T extends ( + el: any, + binding: infer B, + vnode: any, + prev: any, +) => any + ? B + : never + +declare function testDirective< + Value, + Modifiers extends string = string, + Arg extends string = string, +>(): ExtractBinding> + +describe('vmodel', () => { + expectType>( + vModelText, + ) + // @ts-expect-error + expectType>(vModelText) +}) + +describe('custom', () => { + expectType<{ + value: number + oldValue: number | null + arg?: 'Arg' + modifiers: Record<'a' | 'b', boolean> + }>(testDirective()) + + expectType<{ + value: number + oldValue: number | null + arg?: 'Arg' + modifiers: Record<'a' | 'b', boolean> + // @ts-expect-error + }>(testDirective()) + + expectType<{ + value: number + oldValue: number | null + arg?: 'Arg' + modifiers: Record<'a' | 'b', boolean> + // @ts-expect-error + }>(testDirective()) + + expectType<{ + value: number + oldValue: number | null + arg?: 'Arg' + modifiers: Record<'a' | 'b', boolean> + // @ts-expect-error + }>(testDirective()) +}) diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 8c0492e1cef..46e1d59a8e7 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -6,13 +6,17 @@ import type { ComponentOptionsWithArrayProps, ComponentOptionsWithObjectProps, ComponentOptionsWithoutProps, + ComponentProvideOptions, ComputedOptions, MethodOptions, RenderFunction, } from './componentOptions' import type { AllowedComponentProps, + Component, ComponentCustomProps, + GlobalComponents, + GlobalDirectives, SetupContext, } from './component' import type { @@ -29,6 +33,7 @@ import type { CreateComponentPublicInstance, } from './componentPublicInstance' import type { SlotsType } from './componentSlots' +import type { Directive } from './directives' export type PublicProps = VNodeProps & AllowedComponentProps & @@ -55,6 +60,10 @@ export type DefineComponent< Props = ResolveProps, Defaults = ExtractDefaultPropTypes, S extends SlotsType = {}, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, > = ComponentPublicInstanceConstructor< CreateComponentPublicInstance< Props, @@ -69,7 +78,10 @@ export type DefineComponent< Defaults, true, {}, - S + S, + LC & GlobalComponents, + Directives & GlobalDirectives, + Exposed > > & ComponentOptionsBase< @@ -85,7 +97,11 @@ export type DefineComponent< Defaults, {}, string, - S + S, + LC & GlobalComponents, + Directives & GlobalDirectives, + Exposed, + Provide > & PP @@ -166,9 +182,13 @@ export function defineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, - S extends SlotsType = {}, I extends ComponentInjectOptions = {}, II extends string = string, + S extends SlotsType = {}, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, >( options: ComponentOptionsWithoutProps< Props, @@ -182,7 +202,11 @@ export function defineComponent< EE, I, II, - S + S, + LC, + Directives, + Exposed, + Provide >, ): DefineComponent< Props, @@ -197,7 +221,11 @@ export function defineComponent< PublicProps, ResolveProps, ExtractDefaultPropTypes, - S + S, + LC, + Directives, + Exposed, + Provide > // overload 3: object format with array props declaration @@ -216,6 +244,10 @@ export function defineComponent< S extends SlotsType = {}, I extends ComponentInjectOptions = {}, II extends string = string, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, Props = Readonly<{ [key in PropNames]?: any }>, >( options: ComponentOptionsWithArrayProps< @@ -230,7 +262,11 @@ export function defineComponent< EE, I, II, - S + S, + LC, + Directives, + Exposed, + Provide >, ): DefineComponent< Props, @@ -245,7 +281,11 @@ export function defineComponent< PublicProps, ResolveProps, ExtractDefaultPropTypes, - S + S, + LC, + Directives, + Exposed, + Provide > // overload 4: object format with object props declaration @@ -262,9 +302,13 @@ export function defineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, - S extends SlotsType = {}, I extends ComponentInjectOptions = {}, II extends string = string, + S extends SlotsType = {}, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, >( options: ComponentOptionsWithObjectProps< PropsOptions, @@ -278,7 +322,11 @@ export function defineComponent< EE, I, II, - S + S, + LC, + Directives, + Exposed, + Provide >, ): DefineComponent< PropsOptions, @@ -293,7 +341,11 @@ export function defineComponent< PublicProps, ResolveProps, ExtractDefaultPropTypes, - S + S, + LC, + Directives, + Exposed, + Provide > // implementation, close to no-op diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index b8466923a92..68d908310d4 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -86,6 +86,13 @@ import { import type { SchedulerJob } from './scheduler' import type { LifecycleHooks } from './enums' +// Augment GlobalComponents +import type { TeleportProps } from './components/Teleport' +import type { SuspenseProps } from './components/Suspense' +import type { KeepAliveProps } from './components/KeepAlive' +import type { BaseTransitionProps } from './components/BaseTransition' +import type { DefineComponent } from './apiDefineComponent' + export type Data = Record /** @@ -126,6 +133,45 @@ export type ComponentInstance = T extends { new (): ComponentPublicInstance } */ export interface ComponentCustomProps {} +/** + * For globally defined Directives + * Here is an example of adding a directive `VTooltip` as global directive: + * + * @example + * ```ts + * import VTooltip from 'v-tooltip' + * + * declare module '@vue/runtime-core' { + * interface GlobalDirectives { + * VTooltip + * } + * } + * ``` + */ +export interface GlobalDirectives extends Record {} + +/** + * For globally defined Components + * Here is an example of adding a component `RouterView` as global component: + * + * @example + * ```ts + * import { RouterView } from 'vue-router' + * + * declare module '@vue/runtime-core' { + * interface GlobalComponents { + * RouterView + * } + * } + * ``` + */ +export interface GlobalComponents extends Record { + Teleport: DefineComponent + Suspense: DefineComponent + KeepAlive: DefineComponent + BaseTransition: DefineComponent +} + /** * Default allowed non-declared props on component in TSX */ diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 65b952b0b51..da1dfcec0cc 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -112,7 +112,11 @@ export interface ComponentOptionsBase< I extends ComponentInjectOptions = {}, II extends string = string, S extends SlotsType = {}, -> extends LegacyOptions, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, +> extends LegacyOptions, ComponentInternalOptions, ComponentCustomOptions { setup?: ( @@ -136,13 +140,16 @@ export interface ComponentOptionsBase< // Luckily `render()` doesn't need any arguments nor does it care about return // type. render?: Function - components?: Record - directives?: Record + // NOTE: extending both LC and Record allows objects to be forced + // to be of type Component, while still inferring LC generic + components?: LC & Record + // NOTE: extending both Directives and Record allows objects to be forced + // to be of type Directive, while still inferring Directives generic + directives?: Directives & Record inheritAttrs?: boolean emits?: (E | EE[]) & ThisType slots?: S - // TODO infer public instance type based on exposed keys - expose?: string[] + expose?: Exposed[] serverPrefetch?(): void | Promise // Runtime compiler only ----------------------------------------------------- @@ -224,6 +231,10 @@ export type ComponentOptionsWithoutProps< I extends ComponentInjectOptions = {}, II extends string = string, S extends SlotsType = {}, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, PE = Props & EmitsToProps, > = ComponentOptionsBase< PE, @@ -238,7 +249,11 @@ export type ComponentOptionsWithoutProps< {}, I, II, - S + S, + LC, + Directives, + Exposed, + Provide > & { props?: undefined } & ThisType< @@ -255,7 +270,10 @@ export type ComponentOptionsWithoutProps< {}, false, I, - S + S, + LC, + Directives, + Exposed > > @@ -272,6 +290,10 @@ export type ComponentOptionsWithArrayProps< I extends ComponentInjectOptions = {}, II extends string = string, S extends SlotsType = {}, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, Props = Prettify>>, > = ComponentOptionsBase< Props, @@ -286,7 +308,11 @@ export type ComponentOptionsWithArrayProps< {}, I, II, - S + S, + LC, + Directives, + Exposed, + Provide > & { props: PropNames[] } & ThisType< @@ -303,7 +329,10 @@ export type ComponentOptionsWithArrayProps< {}, false, I, - S + S, + LC, + Directives, + Exposed > > @@ -320,6 +349,10 @@ export type ComponentOptionsWithObjectProps< I extends ComponentInjectOptions = {}, II extends string = string, S extends SlotsType = {}, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, Props = Prettify & EmitsToProps>>, Defaults = ExtractDefaultPropTypes, > = ComponentOptionsBase< @@ -335,7 +368,11 @@ export type ComponentOptionsWithObjectProps< Defaults, I, II, - S + S, + LC, + Directives, + Exposed, + Provide > & { props: PropsOptions & ThisType } & ThisType< @@ -352,7 +389,9 @@ export type ComponentOptionsWithObjectProps< Defaults, false, I, - S + S, + LC, + Directives > > @@ -365,7 +404,15 @@ export type ComponentOptions< Mixin extends ComponentOptionsMixin = any, Extends extends ComponentOptionsMixin = any, E extends EmitsOptions = any, - S extends SlotsType = any, + EE extends string = string, + Defaults = {}, + I extends ComponentInjectOptions = {}, + II extends string = string, + S extends SlotsType = {}, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, > = ComponentOptionsBase< Props, RawBindings, @@ -375,8 +422,15 @@ export type ComponentOptions< Mixin, Extends, E, - string, - S + EE, + Defaults, + I, + II, + S, + LC, + Directives, + Exposed, + Provide > & ThisType< CreateComponentPublicInstance< @@ -388,7 +442,13 @@ export type ComponentOptions< Mixin, Extends, E, - Readonly + Readonly, + Defaults, + false, + I, + S, + LC, + Directives > > @@ -403,6 +463,12 @@ export type ComponentOptionsMixin = ComponentOptionsBase< any, any, any, + any, + any, + any, + any, + any, + any, any > @@ -464,6 +530,7 @@ interface LegacyOptions< Extends extends ComponentOptionsMixin, I extends ComponentInjectOptions, II extends string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, > { compatConfig?: CompatConfig @@ -497,7 +564,7 @@ interface LegacyOptions< computed?: C methods?: M watch?: ComponentWatchOptions - provide?: ComponentProvideOptions + provide?: Provide inject?: I | II[] // assets diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index deb9ff69bc3..864b9786efe 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -1,4 +1,5 @@ import { + type Component, type ComponentInternalInstance, type Data, getExposeProxy, @@ -35,6 +36,7 @@ import { type ComponentInjectOptions, type ComponentOptionsBase, type ComponentOptionsMixin, + type ComponentProvideOptions, type ComputedOptions, type ExtractComputedReturns, type InjectToObject, @@ -51,6 +53,7 @@ import { markAttrsAccessed } from './componentRenderUtils' import { currentRenderingInstance } from './componentRenderContext' import { warn } from './warning' import { installCompatInstanceProperties } from './compat/instance' +import type { Directive } from './directives' /** * Custom properties added to component instances in any way and can be accessed through `this` @@ -99,6 +102,10 @@ type MixinToOptionTypes = infer Defaults, any, any, + any, + any, + any, + any, any > ? OptionTypesType

& @@ -157,6 +164,9 @@ export type CreateComponentPublicInstance< MakeDefaultsOptional extends boolean = false, I extends ComponentInjectOptions = {}, S extends SlotsType = {}, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, PublicMixin = IntersectionMixin & IntersectionMixin, PublicP = UnwrapMixinsType & EnsureNonVoid

, PublicB = UnwrapMixinsType & EnsureNonVoid, @@ -167,6 +177,7 @@ export type CreateComponentPublicInstance< EnsureNonVoid, PublicDefaults = UnwrapMixinsType & EnsureNonVoid, + Provide extends ComponentProvideOptions = ComponentProvideOptions, > = ComponentPublicInstance< PublicP, PublicB, @@ -190,11 +201,22 @@ export type CreateComponentPublicInstance< Defaults, {}, string, - S + S, + LC, + Directives, + Exposed, + Provide >, I, - S + S, + Exposed > + +export type ExposedKeys< + T, + Exposed extends string & keyof T, +> = '' extends Exposed ? T : Pick + // public properties exposed on the proxy, which is used as the render context // in templates (as `this` in the render option) export type ComponentPublicInstance< @@ -210,6 +232,7 @@ export type ComponentPublicInstance< Options = ComponentOptionsBase, I extends ComponentInjectOptions = {}, S extends SlotsType = {}, + Exposed extends string = '', > = { $: ComponentInternalInstance $data: D @@ -233,13 +256,16 @@ export type ComponentPublicInstance< : (...args: any) => any, options?: WatchOptions, ): WatchStopHandle -} & IfAny>> & - ShallowUnwrapRef & - UnwrapNestedRefs & - ExtractComputedReturns & - M & - ComponentCustomProperties & - InjectToObject +} & ExposedKeys< + IfAny>> & + ShallowUnwrapRef & + UnwrapNestedRefs & + ExtractComputedReturns & + M & + ComponentCustomProperties & + InjectToObject, + Exposed +> export type PublicPropertiesMap = Record< string, diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts index daaf28b1518..c6dce57c1b6 100644 --- a/packages/runtime-core/src/directives.ts +++ b/packages/runtime-core/src/directives.ts @@ -26,19 +26,29 @@ import { mapCompatDirectiveHook } from './compat/customDirective' import { pauseTracking, resetTracking } from '@vue/reactivity' import { traverse } from './apiWatch' -export interface DirectiveBinding { +export interface DirectiveBinding< + Value = any, + Modifiers extends string = string, + Arg extends string = string, +> { instance: ComponentPublicInstance | null - value: V - oldValue: V | null - arg?: string - modifiers: DirectiveModifiers - dir: ObjectDirective + value: Value + oldValue: Value | null + arg?: Arg + modifiers: DirectiveModifiers + dir: ObjectDirective } -export type DirectiveHook | null, V = any> = ( - el: T, - binding: DirectiveBinding, - vnode: VNode, +export type DirectiveHook< + HostElement = any, + Prev = VNode | null, + Value = any, + Modifiers extends string = string, + Arg extends string = string, +> = ( + el: HostElement, + binding: DirectiveBinding, + vnode: VNode, prevVNode: Prev, ) => void @@ -47,25 +57,52 @@ export type SSRDirectiveHook = ( vnode: VNode, ) => Data | undefined -export interface ObjectDirective { - created?: DirectiveHook - beforeMount?: DirectiveHook - mounted?: DirectiveHook - beforeUpdate?: DirectiveHook, V> - updated?: DirectiveHook, V> - beforeUnmount?: DirectiveHook - unmounted?: DirectiveHook +export interface ObjectDirective< + HostElement = any, + Value = any, + Modifiers extends string = string, + Arg extends string = string, +> { + created?: DirectiveHook + beforeMount?: DirectiveHook + mounted?: DirectiveHook + beforeUpdate?: DirectiveHook< + HostElement, + VNode, + Value, + Modifiers, + Arg + > + updated?: DirectiveHook< + HostElement, + VNode, + Value, + Modifiers, + Arg + > + beforeUnmount?: DirectiveHook + unmounted?: DirectiveHook getSSRProps?: SSRDirectiveHook deep?: boolean } -export type FunctionDirective = DirectiveHook +export type FunctionDirective< + HostElement = any, + V = any, + Modifiers extends string = string, + Arg extends string = string, +> = DirectiveHook -export type Directive = - | ObjectDirective - | FunctionDirective +export type Directive< + HostElement = any, + Value = any, + Modifiers extends string = string, + Arg extends string = string, +> = + | ObjectDirective + | FunctionDirective -export type DirectiveModifiers = Record +export type DirectiveModifiers = Record export function validateDirectiveName(name: string) { if (isBuiltInDirective(name)) { diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 94b2985040a..5d36407ba6a 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -248,6 +248,8 @@ export type { SetupContext, ComponentCustomProps, AllowedComponentProps, + GlobalComponents, + GlobalDirectives, ComponentInstance, } from './component' export type { diff --git a/packages/runtime-core/types/globalComponents.d.ts b/packages/runtime-core/types/globalComponents.d.ts new file mode 100644 index 00000000000..a4abd6d1fff --- /dev/null +++ b/packages/runtime-core/types/globalComponents.d.ts @@ -0,0 +1,11 @@ +// Note: this file is auto concatenated to the end of the bundled d.ts during +// build. + +declare module '@vue/runtime-core' { + export interface GlobalComponents { + Teleport: DefineComponent + Suspense: DefineComponent + KeepAlive: DefineComponent + BaseTransition: DefineComponent + } +} diff --git a/packages/runtime-dom/__tests__/directives/vOn.spec.ts b/packages/runtime-dom/__tests__/directives/vOn.spec.ts index 03620f747e7..ef7ee346ba8 100644 --- a/packages/runtime-dom/__tests__/directives/vOn.spec.ts +++ b/packages/runtime-dom/__tests__/directives/vOn.spec.ts @@ -43,7 +43,7 @@ describe('runtime-dom: v-on directive', () => { }) test('it should support key modifiers and system modifiers', () => { - const keyNames = ['ctrl', 'shift', 'meta', 'alt'] + const keyNames = ['ctrl', 'shift', 'meta', 'alt'] as const keyNames.forEach(keyName => { const el = document.createElement('div') diff --git a/packages/runtime-dom/src/directives/vModel.ts b/packages/runtime-dom/src/directives/vModel.ts index b0ea41728bd..cb599656f0d 100644 --- a/packages/runtime-dom/src/directives/vModel.ts +++ b/packages/runtime-dom/src/directives/vModel.ts @@ -39,14 +39,17 @@ function onCompositionEnd(e: Event) { const assignKey = Symbol('_assign') -type ModelDirective = ObjectDirective< - T & { [assignKey]: AssignerFn; _assigning?: boolean } +type ModelDirective = ObjectDirective< + T & { [assignKey]: AssignerFn; _assigning?: boolean }, + any, + Modifiers > // We are exporting the v-model runtime directly as vnode hooks so that it can // be tree-shaken in case v-model is never used. export const vModelText: ModelDirective< - HTMLInputElement | HTMLTextAreaElement + HTMLInputElement | HTMLTextAreaElement, + 'trim' | 'number' | 'lazy' > = { created(el, { modifiers: { lazy, trim, number } }, vnode) { el[assignKey] = getModelAssigner(vnode) @@ -183,7 +186,7 @@ export const vModelRadio: ModelDirective = { }, } -export const vModelSelect: ModelDirective = { +export const vModelSelect: ModelDirective = { //