From 08bf9976aed985b9ded74eae549fe2a21175cb92 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 9 Nov 2019 18:40:25 -0500 Subject: [PATCH] types: ensure props are readonly --- .../runtime-core/src/apiCreateComponent.ts | 5 ++++- packages/runtime-core/src/apiOptions.ts | 6 ++--- packages/runtime-core/src/componentProps.ts | 10 ++------- test-dts/createComponent.test-d.tsx | 22 ++++++++++++++++--- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/runtime-core/src/apiCreateComponent.ts b/packages/runtime-core/src/apiCreateComponent.ts index f4ab9bd3..bd23f678 100644 --- a/packages/runtime-core/src/apiCreateComponent.ts +++ b/packages/runtime-core/src/apiCreateComponent.ts @@ -14,7 +14,10 @@ import { VNodeProps } from './vnode' // overload 1: direct setup function // (uses user defined props interface) export function createComponent( - setup: (props: Props, ctx: SetupContext) => RawBindings | RenderFunction + setup: ( + props: Readonly, + ctx: SetupContext + ) => RawBindings | RenderFunction ): { new (): ComponentPublicInstance< Props, diff --git a/packages/runtime-core/src/apiOptions.ts b/packages/runtime-core/src/apiOptions.ts index 829dfdcc..d6068d8d 100644 --- a/packages/runtime-core/src/apiOptions.ts +++ b/packages/runtime-core/src/apiOptions.ts @@ -83,7 +83,7 @@ export type ComponentOptionsWithoutProps< M extends MethodOptions = {} > = ComponentOptionsBase & { props?: undefined -} & ThisType> +} & ThisType>> export type ComponentOptionsWithArrayProps< PropNames extends string = string, @@ -91,7 +91,7 @@ export type ComponentOptionsWithArrayProps< D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, - Props = { [key in PropNames]?: any } + Props = Readonly<{ [key in PropNames]?: any }> > = ComponentOptionsBase & { props: PropNames[] } & ThisType> @@ -102,7 +102,7 @@ export type ComponentOptionsWithObjectProps< D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, - Props = ExtractPropTypes + Props = Readonly> > = ComponentOptionsBase & { props: PropsOptions } & ThisType> diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 41ce3096..ead0784d 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -65,14 +65,8 @@ export type ExtractPropTypes< O, MakeDefaultRequired extends boolean = true > = O extends object - ? { - readonly [K in RequiredKeys]: InferPropType - } & - { - readonly [K in OptionalKeys]?: InferPropType< - O[K] - > - } + ? { [K in RequiredKeys]: InferPropType } & + { [K in OptionalKeys]?: InferPropType } : { [K in string]: any } const enum BooleanFlags { diff --git a/test-dts/createComponent.test-d.tsx b/test-dts/createComponent.test-d.tsx index 7d563207..ce15c704 100644 --- a/test-dts/createComponent.test-d.tsx +++ b/test-dts/createComponent.test-d.tsx @@ -71,6 +71,9 @@ describe('with object props', () => { expectType(props.ccc) expectType(props.ddd) + // props should be readonly + expectError((props.a = 1)) + // should also expose declared props on `this` expectType(this.a) expectType(this.b) @@ -80,10 +83,16 @@ describe('with object props', () => { expectType(this.ccc) expectType(this.ddd) + // props on `this` should be readonly + expectError((this.a = 1)) + // assert setup context unwrapping expectType(this.c) expectType(this.d.e) + // setup context properties should be mutable + this.c = 2 + return null } }) @@ -126,6 +135,9 @@ describe('type inference w/ optional props declaration', () => { }, render() { expectType(this.$props.msg) + // props should be readonly + expectError((this.$props.msg = 'foo')) + // should not expose on `this` expectError(this.msg) expectType(this.a) return null @@ -148,14 +160,18 @@ describe('type inference w/ array props declaration', () => { createComponent({ props: ['a', 'b'], setup(props) { - props.a - props.b + // props should be readonly + expectError((props.a = 1)) + expectType(props.a) + expectType(props.b) return { c: 1 } }, render() { - expectType<{ a?: any; b?: any }>(this.$props) + expectType(this.$props.a) + expectType(this.$props.b) + expectError((this.$props.a = 1)) expectType(this.a) expectType(this.b) expectType(this.c)