From 824ea5ddab19f80c651c4aebe8921d5522415ac4 Mon Sep 17 00:00:00 2001 From: rudyxu Date: Sun, 1 Jan 2023 18:38:58 +0800 Subject: [PATCH 01/12] docs: infer attrs rfc --- active-rfcs/0000-infer-attrs.md | 147 ++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 active-rfcs/0000-infer-attrs.md diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md new file mode 100644 index 00000000..92f5558c --- /dev/null +++ b/active-rfcs/0000-infer-attrs.md @@ -0,0 +1,147 @@ +- Start Date: 2023-01-01 +- Target Major Version: 3.x +- Reference Issues: [vuejs/core#3452](https://github.com/vuejs/core/issues/3452), [vuejs/core#5423](https://github.com/vuejs/core/issues/5423), +- Implementation PR: [vuejs/core#7444](https://github.com/vuejs/core/pull/7444) + +# Summary +Allow to infer `attrs` when passing type on the second param of `defineComponent` and `defineCustomElement`. +And in the `setup-script`, passing generic type on the `useAttrs` will infer `attrs` to `T`. + +I already published an npm package named [vue-ts-utils](https://github.com/rudy-xhd/vue-ts-utils), so that you can use `defineComponent` to infer attrs in advance. + +# Basic example + +## Using `defineComponent` +[TS Playground](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAbzgEwKYDNgDtUGELgQ5bwC+c6UBcA5AG4CuqAtDAM7MMzAA2bNAWABQwgMZE28fODgBeFBmx4CkYjAAUCYXDhgqYNgC5E2nRQgRjAZRhRsAcwA0p0s6E6oqLGijqAlIikwq6IcACGMLZGgeFsoQBGYVDGWAwg8ahQcOSkfsJiEvDiMvIAPNJg5hCyCDQAjDTkiVA1deQA9AB8wkA) +```tsx +const Comp = defineComponent({ + props: { + foo: String + }, + render() { + // number + console.log(this.$attrs.bar) + } +}, { attrs: {} as { bar: number } }) + +const comp = +``` + +## Using `useAttrs` in `script-setup` + +```vue + +``` + +
+Compiled Output + +```js +export default /*#__PURE__*/_defineComponent({ + setup(__props, { expose }) { + expose(); + + const attrs = useAttrs<{ foo: number }>() + +return { attrs, useAttrs, ref } +} + +}, { attrs: {} as { foo: number }})" +``` + +
+ + +## Using `defineCustomElement` +```tsx +const Comp = defineCustomElement({ + props: { + foo: String + }, + render() { + // number + console.log(this.$attrs.bar) + } +}, { attrs: {} as { bar: number } }) + +``` + +# Motivation +This proposal is mainly to infer `attrs` using `defineComponent`. + +When using typescript in Vue3, the fallthrough attributes is unable to be used. It's not appropriate obviously that only one can be chosen from `typescript` and `the fallthrough attributes`. In most cases, we choose `typescript` and set attributes to `props` options instead of using the fallthrough attributes. + + +# Detailed design + +## `defineComponent` +Due to typescript limitation from [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571), it's not possible to skip generics up to now in the `defineComponent` like below. +```tsx +// it's not work +const Comp = defineComponent({}) +``` + +There still has two ways to be chosen. + +1. Defining the first param that already existing, just like [vuejs/rfcs#192](https://github.com/vuejs/rfcs/pull/192) did. +```tsx +const Comp = defineComponent({ + attrs: {} as { bar: number }, + props: { + foo: String + }, + render() { + // number + console.log(this.$attrs.bar) + } +}) + +const comp = +``` +2. Defining the second param as proposed. +```tsx +const Comp = defineComponent({ + props: { + foo: String + }, + render() { + // number + console.log(this.$attrs.bar) + } +}, { attrs: {} as { bar: number } }) + +const comp = +``` + +At last i chosen the second way that pass `attrs` type to the second params of `defineComponent`, because I think the code of the component should not be involved just for type definition. + + +The following below is the design details. +- `attrs` is inferred to `{ class: unknown; style: unknown }` when the value of the second param is `undefined` +- `attrs` is lower priority than `props`. +- [see for more detail cases](https://github.com/vuejs/core/pull/7444/files) + +## `useAttrs` +In the `setup-script`, the generic type of `useAttrs` will compile to the second param of `defineComponent`. +```ts +export default /*#__PURE__*/_defineComponent({ + setup(__props, { expose }) { + expose(); + + const attrs = useAttrs<{ foo: number }>() + +return { attrs, useAttrs, ref } +} + +}, { attrs: {} as { foo: number }})" +``` + +## `defineCustomElement` +The type inferrence of `defineCustomElement` is the same as `defineComponent`. + + +# Unresolved questions +Naming suggestions or improvements on the API are welcome. + From a71a63fb7c66e92c9dba5ca82c77cb8b75f1f222 Mon Sep 17 00:00:00 2001 From: rudy-xhd Date: Mon, 2 Jan 2023 11:25:44 +0800 Subject: [PATCH 02/12] fix: add scene --- active-rfcs/0000-infer-attrs.md | 87 ++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index 92f5558c..c51b0fed 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -1,6 +1,6 @@ - Start Date: 2023-01-01 - Target Major Version: 3.x -- Reference Issues: [vuejs/core#3452](https://github.com/vuejs/core/issues/3452), [vuejs/core#5423](https://github.com/vuejs/core/issues/5423), +- Reference Issues: [vuejs/core#3452](https://github.com/vuejs/core/issues/3452), [vuejs/core#5423](https://github.com/vuejs/core/issues/5423), [vuejs/core#6528](https://github.com/vuejs/core/discussions/6528) - Implementation PR: [vuejs/core#7444](https://github.com/vuejs/core/pull/7444) # Summary @@ -12,7 +12,7 @@ I already published an npm package named [vue-ts-utils](https://github.com/rudy- # Basic example ## Using `defineComponent` -[TS Playground](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAbzgEwKYDNgDtUGELgQ5bwC+c6UBcA5AG4CuqAtDAM7MMzAA2bNAWABQwgMZE28fODgBeFBmx4CkYjAAUCYXDhgqYNgC5E2nRQgRjAZRhRsAcwA0p0s6E6oqLGijqAlIikwq6IcACGMLZGgeFsoQBGYVDGWAwg8ahQcOSkfsJiEvDiMvIAPNJg5hCyCDQAjDTkiVA1deQA9AB8wkA) +[TS Playground](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAbzgEwKYDNgDtUGELgQ5bwC+c6UBcA5AG4CuqAtDAM7MMzAA2bNAWABQwgMZE28fODgBeFBmx4CkYjAAUCYXDhgqYNgC5E2nRQgRjAZRhRsAcwA0p0s6E6oqLGijqAlIikwq6IcACGMLZGgeFsoQBGYVDGWAwg8ahQcOSkfsJiEvDiMvIAPNJg5hCyCDQAjDTkiVA1deQA9AB8wkA) with Options Api ```tsx const Comp = defineComponent({ props: { @@ -27,32 +27,59 @@ const Comp = defineComponent({ const comp = ``` -## Using `useAttrs` in `script-setup` +[TS Playground](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAbzgEwKYDNgDtUGELgQ5bwC+c6UBcA5AG4CuqAtDAM7MMzAA2bNAWABQwgMZE28fODgBeFBmx4CkYjAAUCYXDhgqYNgC5E2nRQgRjAZRhRsAcwA0p0s6E62qGAzDq9EA0dEOABDGFs2OFIAShN3M3EsNggeVAA6Hgh7P302NPQLWIB6IrhJOyx7Ux1E5NSMrPUwiLSAIxCoYtKsBhBW1CgXYVdg5qgjRHIQyKR2qGMevoGoqOjhMQl4cRl5AB5pMHMIWQQaAEYacjmTs-IigD5hIA) with Composition Api +```tsx +const Comp = defineComponent({ + props: { + foo: String + }, + setup(props, { attrs }) { + // number + console.log(attrs.bar) + } +}, { attrs: {} as { bar: number } }) -```vue - +const comp = ``` -
-Compiled Output -```js -export default /*#__PURE__*/_defineComponent({ - setup(__props, { expose }) { - expose(); +[TS Playground](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAbzgEwKYDNgDtUGELgQ5bwC+c6UBcA5AG4CuqAtDAM7MMzAA2bNAWABQwgMZE28fODgBeFBmx4CkYjAAUCYXDhgqYNgC5E2nRQgRjAZRhRsAcwA0p0s6E62qGAzDq9EA0dEOABDGFs2OFIAShN3M3EsNggeVAA6Hgh7P302NPQLWIB6IrhJOyx7Ux1E5NSMrPUwiLSAIxCoYtKsBhBW1CgXYVdg5qgjRHIQyKR2qGMevoGoqOjhMQl4cRl5AB5pMHMIWQQaAEYacjmTs-IigD5hIA) with functional Components +```tsx +import { defineComponent, h, type SetupContext } from 'vue' - const attrs = useAttrs<{ foo: number }>() +type CompAttrs = { + bar: number + baz?: string +} -return { attrs, useAttrs, ref } +type CompEmits = { + change: (val: string) => void; } -}, { attrs: {} as { foo: number }})" +const MyComp = defineComponent( + (_props: { foo: string }, ctx: SetupContext) => { + // number + console.log(ctx.attrs.bar) + // string | undefined + console.log(ctx.attrs.baz) + + ctx.emit('change', '1') + + return h('div') + } +) + +const comp = ``` -
+## Using `useAttrs` in `script-setup` + +```vue + +``` ## Using `defineCustomElement` ```tsx @@ -71,8 +98,12 @@ const Comp = defineCustomElement({ # Motivation This proposal is mainly to infer `attrs` using `defineComponent`. -When using typescript in Vue3, the fallthrough attributes is unable to be used. It's not appropriate obviously that only one can be chosen from `typescript` and `the fallthrough attributes`. In most cases, we choose `typescript` and set attributes to `props` options instead of using the fallthrough attributes. +When using typescript in Vue3, the fallthrough attributes is unable to be used. It's not appropriate obviously that only one can be chosen from `typescript` and `the fallthrough attributes`. In most cases, we choose `typescript` and set attributes to `props` option instead of using the fallthrough attributes. +Main scenes: + +- Enhancing native html component, such as `button`, `input`... +- Wrapping a component from UI library, such as `el-button` from [element-plus](https://github.com/element-plus/element-plus). # Detailed design @@ -83,9 +114,10 @@ Due to typescript limitation from [microsoft/TypeScript#10571](https://github.co const Comp = defineComponent({}) ``` + There still has two ways to be chosen. -1. Defining the first param that already existing, just like [vuejs/rfcs#192](https://github.com/vuejs/rfcs/pull/192) did. +### 1. Defining the first param that already existing, just like [vuejs/rfcs#192](https://github.com/vuejs/rfcs/pull/192) did. ```tsx const Comp = defineComponent({ attrs: {} as { bar: number }, @@ -100,7 +132,7 @@ const Comp = defineComponent({ const comp = ``` -2. Defining the second param as proposed. +### 2. Defining the second param as proposed. ```tsx const Comp = defineComponent({ props: { @@ -115,17 +147,26 @@ const Comp = defineComponent({ const comp = ``` -At last i chosen the second way that pass `attrs` type to the second params of `defineComponent`, because I think the code of the component should not be involved just for type definition. +At last I chosen the second way that passing `attrs` type to the second params of `defineComponent`, because I think the code of the component should not be involved just for type definition. The following below is the design details. - `attrs` is inferred to `{ class: unknown; style: unknown }` when the value of the second param is `undefined` - `attrs` is lower priority than `props`. -- [see for more detail cases](https://github.com/vuejs/core/pull/7444/files) +- [see for more detail cases](https://github.com/vuejs/core/pull/7444/files#diff-241bba82b0b4ebadd7a9c19ed82eed97283874b6d15ed32d62c05184e29ecb91R1195-R1306) ## `useAttrs` In the `setup-script`, the generic type of `useAttrs` will compile to the second param of `defineComponent`. -```ts + +```vue + +``` + +Compiled Output: + +```js export default /*#__PURE__*/_defineComponent({ setup(__props, { expose }) { expose(); From 4d3892cf27d70ebc15f1afba6015a8a33990b93c Mon Sep 17 00:00:00 2001 From: rudy-xhd Date: Wed, 4 Jan 2023 00:10:24 +0800 Subject: [PATCH 03/12] fix: typo --- active-rfcs/0000-infer-attrs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index c51b0fed..e8bc63c6 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -171,12 +171,12 @@ export default /*#__PURE__*/_defineComponent({ setup(__props, { expose }) { expose(); - const attrs = useAttrs<{ foo: number }>() + const attrs = useAttrs<{ bar: number }>() return { attrs, useAttrs, ref } } -}, { attrs: {} as { foo: number }})" +}, { attrs: {} as { bar: number }})" ``` ## `defineCustomElement` From 164f2f8eaa778a80d004ec04230f6c054f505f2d Mon Sep 17 00:00:00 2001 From: rudy-xhd Date: Wed, 4 Jan 2023 00:14:35 +0800 Subject: [PATCH 04/12] fix: typo --- active-rfcs/0000-infer-attrs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index e8bc63c6..0678c5ce 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -102,8 +102,8 @@ When using typescript in Vue3, the fallthrough attributes is unable to be used. Main scenes: -- Enhancing native html component, such as `button`, `input`... -- Wrapping a component from UI library, such as `el-button` from [element-plus](https://github.com/element-plus/element-plus). +- Wrapping an native component in a new component, such as `button`, `input`... +- Wrapping a component from UI library in a new component, such as `el-button` from [element-plus](https://github.com/element-plus/element-plus). # Detailed design From d5000980cef6ebd353ede6e22986cd4d5e0088bc Mon Sep 17 00:00:00 2001 From: rudy-xhd Date: Wed, 4 Jan 2023 23:53:55 +0800 Subject: [PATCH 05/12] fix: typo --- active-rfcs/0000-infer-attrs.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index 0678c5ce..243652df 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -4,8 +4,8 @@ - Implementation PR: [vuejs/core#7444](https://github.com/vuejs/core/pull/7444) # Summary -Allow to infer `attrs` when passing type on the second param of `defineComponent` and `defineCustomElement`. -And in the `setup-script`, passing generic type on the `useAttrs` will infer `attrs` to `T`. +Allowing to infer `attrs` by passing the second param of `defineComponent` or `defineCustomElement`. +And in the `setup-script`, passing generic type in the `useAttrs` will also infer `attrs` to `T`. I already published an npm package named [vue-ts-utils](https://github.com/rudy-xhd/vue-ts-utils), so that you can use `defineComponent` to infer attrs in advance. @@ -98,17 +98,17 @@ const Comp = defineCustomElement({ # Motivation This proposal is mainly to infer `attrs` using `defineComponent`. -When using typescript in Vue3, the fallthrough attributes is unable to be used. It's not appropriate obviously that only one can be chosen from `typescript` and `the fallthrough attributes`. In most cases, we choose `typescript` and set attributes to `props` option instead of using the fallthrough attributes. +When using typescript in Vue3, the fallthrough attributes is unable to be used. It's not appropriate obviously that only one can be chosen from `typescript` and `Fallthrough Attributes`. In most cases, we choose `typescript` and set attributes to `props` option instead of using the fallthrough attributes. Main scenes: -- Wrapping an native component in a new component, such as `button`, `input`... -- Wrapping a component from UI library in a new component, such as `el-button` from [element-plus](https://github.com/element-plus/element-plus). +- Wrapping an native component in a new component, such as `button`. [Here is a demo to describe](https://github.com/rudy-xhd/vue-demo/tree/native-button). +- Wrapping a component from UI library in a new component, such as `el-button` from `element-plus`. [Here is a demo to describe](https://github.com/rudy-xhd/vue-demo/tree/ui-button). # Detailed design ## `defineComponent` -Due to typescript limitation from [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571), it's not possible to skip generics up to now in the `defineComponent` like below. +Due to typescript limitation from [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571) and [stackoverflow/infer-type-argument-from-function-argument-in-typescript](https://stackoverflow.com/questions/57195611/infer-type-argument-from-function-argument-in-typescript), it's not possible to make generics partial in the `defineComponent` up to now. ```tsx // it's not work const Comp = defineComponent({}) @@ -149,11 +149,7 @@ const comp = At last I chosen the second way that passing `attrs` type to the second params of `defineComponent`, because I think the code of the component should not be involved just for type definition. - -The following below is the design details. -- `attrs` is inferred to `{ class: unknown; style: unknown }` when the value of the second param is `undefined` -- `attrs` is lower priority than `props`. -- [see for more detail cases](https://github.com/vuejs/core/pull/7444/files#diff-241bba82b0b4ebadd7a9c19ed82eed97283874b6d15ed32d62c05184e29ecb91R1195-R1306) +> [see for more detail cases](https://github.com/vuejs/core/pull/7444/files#diff-241bba82b0b4ebadd7a9c19ed82eed97283874b6d15ed32d62c05184e29ecb91R1195) ## `useAttrs` In the `setup-script`, the generic type of `useAttrs` will compile to the second param of `defineComponent`. From 75b3c6968df9450caa3d5a2524f788dffd51b8b8 Mon Sep 17 00:00:00 2001 From: rudy-xhd Date: Thu, 5 Jan 2023 23:33:50 +0800 Subject: [PATCH 06/12] fix: typo --- active-rfcs/0000-infer-attrs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index 243652df..bc29d744 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -102,7 +102,7 @@ When using typescript in Vue3, the fallthrough attributes is unable to be used. Main scenes: -- Wrapping an native component in a new component, such as `button`. [Here is a demo to describe](https://github.com/rudy-xhd/vue-demo/tree/native-button). +- Wrapping a native HTML element in a new component, such as `button`. [Here is a demo to describe](https://github.com/rudy-xhd/vue-demo/tree/native-button). - Wrapping a component from UI library in a new component, such as `el-button` from `element-plus`. [Here is a demo to describe](https://github.com/rudy-xhd/vue-demo/tree/ui-button). # Detailed design From 5a55ac260aeeedf69a62578251a71bcbb0d310c8 Mon Sep 17 00:00:00 2001 From: rudy-xhd Date: Fri, 6 Jan 2023 00:16:25 +0800 Subject: [PATCH 07/12] fix: typo --- active-rfcs/0000-infer-attrs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index bc29d744..82ea1da4 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -108,14 +108,14 @@ Main scenes: # Detailed design ## `defineComponent` -Due to typescript limitation from [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571) and [stackoverflow/infer-type-argument-from-function-argument-in-typescript](https://stackoverflow.com/questions/57195611/infer-type-argument-from-function-argument-in-typescript), it's not possible to make generics partial in the `defineComponent` up to now. +Due to typescript limitation from [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571), it's not possible to make generics partial in the `defineComponent` up to now. To be more clear, there is a similar question from [stackoverflow/infer-type-argument-from-function-argument-in-typescript](https://stackoverflow.com/questions/57195611/infer-type-argument-from-function-argument-in-typescript) ```tsx // it's not work const Comp = defineComponent({}) ``` -There still has two ways to be chosen. +But there still has two ways to be chosen personally. ### 1. Defining the first param that already existing, just like [vuejs/rfcs#192](https://github.com/vuejs/rfcs/pull/192) did. ```tsx From 7d25468007f629c48dd02ca143d4bd725f0f34b1 Mon Sep 17 00:00:00 2001 From: rudy-xhd Date: Fri, 6 Jan 2023 00:18:25 +0800 Subject: [PATCH 08/12] fix: typo --- active-rfcs/0000-infer-attrs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index 82ea1da4..f7f52fb9 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -108,7 +108,7 @@ Main scenes: # Detailed design ## `defineComponent` -Due to typescript limitation from [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571), it's not possible to make generics partial in the `defineComponent` up to now. To be more clear, there is a similar question from [stackoverflow/infer-type-argument-from-function-argument-in-typescript](https://stackoverflow.com/questions/57195611/infer-type-argument-from-function-argument-in-typescript) +Due to typescript limitation from [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571), it's not possible to make generics partial in the `defineComponent` up to now. To be more clear, there is a similar question from [stackoverflow/infer-type-argument-from-function-argument-in-typescript](https://stackoverflow.com/questions/57195611/infer-type-argument-from-function-argument-in-typescript). ```tsx // it's not work const Comp = defineComponent({}) From 8716e2d9318336239f87d06017784bad893de1f5 Mon Sep 17 00:00:00 2001 From: rudy-xhd Date: Fri, 6 Jan 2023 00:20:02 +0800 Subject: [PATCH 09/12] fix: typo --- active-rfcs/0000-infer-attrs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index f7f52fb9..53dd956b 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -115,7 +115,7 @@ const Comp = defineComponent({}) ``` -But there still has two ways to be chosen personally. +But there still has two ways to be chosen. ### 1. Defining the first param that already existing, just like [vuejs/rfcs#192](https://github.com/vuejs/rfcs/pull/192) did. ```tsx From 44496ce009cbd0c34026b014ae2d8166e9d6ac7a Mon Sep 17 00:00:00 2001 From: Rudy Date: Wed, 30 Aug 2023 21:38:12 +0800 Subject: [PATCH 10/12] Update 0000-infer-attrs.md --- active-rfcs/0000-infer-attrs.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index 53dd956b..9c427ec5 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -182,3 +182,7 @@ The type inferrence of `defineCustomElement` is the same as `defineComponent`. # Unresolved questions Naming suggestions or improvements on the API are welcome. + +# Updated +Since slots option has aleady supported, I think attrs option may be more appropriate instead of using the second params as the rfcs said. + From 8182b714dd61e0bc124cf03e818ef58405c8d703 Mon Sep 17 00:00:00 2001 From: Rudy Date: Sat, 14 Oct 2023 21:08:50 +0800 Subject: [PATCH 11/12] Update 0000-infer-attrs.md --- active-rfcs/0000-infer-attrs.md | 234 +++++++++++++++----------------- 1 file changed, 108 insertions(+), 126 deletions(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index 9c427ec5..2a779a4f 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -4,95 +4,97 @@ - Implementation PR: [vuejs/core#7444](https://github.com/vuejs/core/pull/7444) # Summary -Allowing to infer `attrs` by passing the second param of `defineComponent` or `defineCustomElement`. -And in the `setup-script`, passing generic type in the `useAttrs` will also infer `attrs` to `T`. - -I already published an npm package named [vue-ts-utils](https://github.com/rudy-xhd/vue-ts-utils), so that you can use `defineComponent` to infer attrs in advance. +Allowing to infer attrs by using `attrs` option of `defineComponent` or `defineCustomElement`. +And in the `setup-script`, passing generic type in the `defineAttrs` will also infer `attrs` to `T`. # Basic example ## Using `defineComponent` -[TS Playground](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAbzgEwKYDNgDtUGELgQ5bwC+c6UBcA5AG4CuqAtDAM7MMzAA2bNAWABQwgMZE28fODgBeFBmx4CkYjAAUCYXDhgqYNgC5E2nRQgRjAZRhRsAcwA0p0s6E6oqLGijqAlIikwq6IcACGMLZGgeFsoQBGYVDGWAwg8ahQcOSkfsJiEvDiMvIAPNJg5hCyCDQAjDTkiVA1deQA9AB8wkA) with Options Api +Options Api ```tsx const Comp = defineComponent({ props: { foo: String }, - render() { - // number - console.log(this.$attrs.bar) + attrs: Object as AttrsType<{ + bar?: number + }>, + created() { + this.$attrs.bar // number | undefined } -}, { attrs: {} as { bar: number } }) +}); -const comp = +; ``` -[TS Playground](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAbzgEwKYDNgDtUGELgQ5bwC+c6UBcA5AG4CuqAtDAM7MMzAA2bNAWABQwgMZE28fODgBeFBmx4CkYjAAUCYXDhgqYNgC5E2nRQgRjAZRhRsAcwA0p0s6E62qGAzDq9EA0dEOABDGFs2OFIAShN3M3EsNggeVAA6Hgh7P302NPQLWIB6IrhJOyx7Ux1E5NSMrPUwiLSAIxCoYtKsBhBW1CgXYVdg5qgjRHIQyKR2qGMevoGoqOjhMQl4cRl5AB5pMHMIWQQaAEYacjmTs-IigD5hIA) with Composition Api +Composition Api ```tsx const Comp = defineComponent({ props: { - foo: String + foo: { + type: String, + required: true + } }, + attrs: Object as AttrsType<{ + bar?: number + }>, setup(props, { attrs }) { - // number - console.log(attrs.bar) + props.foo; // string + attrs.bar; // number | undefined } -}, { attrs: {} as { bar: number } }) - -const comp = +}); +; ``` - - -[TS Playground](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAbzgEwKYDNgDtUGELgQ5bwC+c6UBcA5AG4CuqAtDAM7MMzAA2bNAWABQwgMZE28fODgBeFBmx4CkYjAAUCYXDhgqYNgC5E2nRQgRjAZRhRsAcwA0p0s6E62qGAzDq9EA0dEOABDGFs2OFIAShN3M3EsNggeVAA6Hgh7P302NPQLWIB6IrhJOyx7Ux1E5NSMrPUwiLSAIxCoYtKsBhBW1CgXYVdg5qgjRHIQyKR2qGMevoGoqOjhMQl4cRl5AB5pMHMIWQQaAEYacjmTs-IigD5hIA) with functional Components +Functional Components ```tsx -import { defineComponent, h, type SetupContext } from 'vue' - -type CompAttrs = { - bar: number - baz?: string -} - -type CompEmits = { - change: (val: string) => void; -} - -const MyComp = defineComponent( - (_props: { foo: string }, ctx: SetupContext) => { - // number - console.log(ctx.attrs.bar) - // string | undefined - console.log(ctx.attrs.baz) - - ctx.emit('change', '1') - - return h('div') +const Comp = defineComponent( + (props: { foo: string }, ctx) => { + ctx.attrs.bar; // number | undefined + return () => ( +
{props.foo}
+ ) + }, + { + attrs: Object as AttrsType<{ + bar?: number + }> } -) - -const comp = +); +; ``` -## Using `useAttrs` in `script-setup` +## Using `defineAttrs` in `setup-script` ```vue +// MyImg.vue +``` +```vue +// MyButton.vue + ``` ## Using `defineCustomElement` ```tsx -const Comp = defineCustomElement({ +const Comp = defineComponent({ props: { foo: String }, - render() { - // number - console.log(this.$attrs.bar) + attrs: Object as AttrsType<{ + bar?: number + }>, + created() { + this.$attrs.bar // number | undefined } -}, { attrs: {} as { bar: number } }) - +}); ``` # Motivation @@ -102,87 +104,67 @@ When using typescript in Vue3, the fallthrough attributes is unable to be used. Main scenes: -- Wrapping a native HTML element in a new component, such as `button`. [Here is a demo to describe](https://github.com/rudy-xhd/vue-demo/tree/native-button). -- Wrapping a component from UI library in a new component, such as `el-button` from `element-plus`. [Here is a demo to describe](https://github.com/rudy-xhd/vue-demo/tree/ui-button). - -# Detailed design - -## `defineComponent` -Due to typescript limitation from [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571), it's not possible to make generics partial in the `defineComponent` up to now. To be more clear, there is a similar question from [stackoverflow/infer-type-argument-from-function-argument-in-typescript](https://stackoverflow.com/questions/57195611/infer-type-argument-from-function-argument-in-typescript). +- Wrapping a native HTML element in a new component, such as `img`. ```tsx -// it's not work -const Comp = defineComponent({}) +import { defineComponent, type ImgHTMLAttributes, type AttrsType } from 'vue'; + +const MyImg = defineComponent({ + props: { + foo: String + }, + attrs: Object as AttrsType, + created() { + this.$attrs.class // any + this.$attrs.style // StyleValue | undefined + this.$attrs.onError // ((payload: Event) => void) | undefined + this.$attrs.src // string | undefined + }, + render() { + return + } +}); + + { + e; // Event +}}/>; ``` - - -But there still has two ways to be chosen. - -### 1. Defining the first param that already existing, just like [vuejs/rfcs#192](https://github.com/vuejs/rfcs/pull/192) did. +- Wrapping a component from UI library in a new component, such as `el-button` from `element-plus`. ```tsx -const Comp = defineComponent({ - attrs: {} as { bar: number }, - props: { - foo: String - }, - render() { - // number - console.log(this.$attrs.bar) - } -}) +import { defineComponent, type AttrsType } from 'vue'; -const comp = -``` -### 2. Defining the second param as proposed. -```tsx const Comp = defineComponent({ - props: { - foo: String - }, - render() { - // number - console.log(this.$attrs.bar) - } -}, { attrs: {} as { bar: number } }) - -const comp = -``` - -At last I chosen the second way that passing `attrs` type to the second params of `defineComponent`, because I think the code of the component should not be involved just for type definition. - -> [see for more detail cases](https://github.com/vuejs/core/pull/7444/files#diff-241bba82b0b4ebadd7a9c19ed82eed97283874b6d15ed32d62c05184e29ecb91R1195) - -## `useAttrs` -In the `setup-script`, the generic type of `useAttrs` will compile to the second param of `defineComponent`. - -```vue - + props: { + foo: String + }, + emits: { + baz: (val: number) => true + }, + render() { + return
{this.foo}
+ } +}); + +const MyComp = defineComponent({ + props: { + bar: Number + }, + attrs: Object as AttrsType, + created() { + this.$attrs.class // unknown + this.$attrs.style // unknown + this.$attrs.onBaz; // ((val: number) => any) | undefined + this.$attrs.foo; // string | undefined + }, + render() { + return + } +}); + + { + val; // number +}} />; ``` -Compiled Output: - -```js -export default /*#__PURE__*/_defineComponent({ - setup(__props, { expose }) { - expose(); - - const attrs = useAttrs<{ bar: number }>() - -return { attrs, useAttrs, ref } -} - -}, { attrs: {} as { bar: number }})" -``` - -## `defineCustomElement` -The type inferrence of `defineCustomElement` is the same as `defineComponent`. - - # Unresolved questions Naming suggestions or improvements on the API are welcome. - -# Updated -Since slots option has aleady supported, I think attrs option may be more appropriate instead of using the second params as the rfcs said. - From cc65979628514f89bbebad33802474edcd76cb9a Mon Sep 17 00:00:00 2001 From: Rudy Date: Mon, 16 Oct 2023 23:23:16 +0800 Subject: [PATCH 12/12] Update `defineAttrs` --- active-rfcs/0000-infer-attrs.md | 72 +++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/active-rfcs/0000-infer-attrs.md b/active-rfcs/0000-infer-attrs.md index 2a779a4f..7a9ed38d 100644 --- a/active-rfcs/0000-infer-attrs.md +++ b/active-rfcs/0000-infer-attrs.md @@ -1,7 +1,20 @@ - Start Date: 2023-01-01 - Target Major Version: 3.x -- Reference Issues: [vuejs/core#3452](https://github.com/vuejs/core/issues/3452), [vuejs/core#5423](https://github.com/vuejs/core/issues/5423), [vuejs/core#6528](https://github.com/vuejs/core/discussions/6528) -- Implementation PR: [vuejs/core#7444](https://github.com/vuejs/core/pull/7444) +- Reference Issues: + - https://github.com/vuejs/core/issues/3452 + - https://github.com/vuejs/core/issues/5423 + - https://github.com/vuejs/core/discussions/6528 + - https://github.com/vuejs/core/discussions/7546 + - https://github.com/vuejs/core/discussions/7981 + - https://github.com/vuejs/core/discussions/8264 + - https://github.com/vuejs/core/discussions/8508 + - https://github.com/vuejs/core/discussions/8620 + - https://github.com/vuejs/core/discussions/8902 + - https://github.com/vuejs/core/discussions/9272 +- Implementation PR: + - https://github.com/vuejs/core/pull/7444 + - https://github.com/vuejs/core/pull/9413 + - Volar/vue-tsc # Summary Allowing to infer attrs by using `attrs` option of `defineComponent` or `defineCustomElement`. @@ -70,18 +83,30 @@ const Comp = defineComponent( ```vue // MyImg.vue ``` -```vue -// MyButton.vue - +
+Complie Output: + +```tsx +import { useAttrs as _useAttrs, defineComponent as _defineComponent } from 'vue' + +export default /*#__PURE__*/_defineComponent({ + setup(__props, { expose: __expose }) { + __expose(); + + const attrs = _useAttrs() + +return { attrs } +} + +}) ``` +
+ + ## Using `defineCustomElement` ```tsx const Comp = defineComponent({ @@ -102,9 +127,11 @@ This proposal is mainly to infer `attrs` using `defineComponent`. When using typescript in Vue3, the fallthrough attributes is unable to be used. It's not appropriate obviously that only one can be chosen from `typescript` and `Fallthrough Attributes`. In most cases, we choose `typescript` and set attributes to `props` option instead of using the fallthrough attributes. -Main scenes: +## Main scenes: + +### 1. Wrapping a native HTML element in a new component, such as `img`. -- Wrapping a native HTML element in a new component, such as `img`. +- Option Api ```tsx import { defineComponent, type ImgHTMLAttributes, type AttrsType } from 'vue'; @@ -128,7 +155,18 @@ const MyImg = defineComponent({ e; // Event }}/>; ``` -- Wrapping a component from UI library in a new component, such as `el-button` from `element-plus`. +- Setup Script +```vue +// MyImg.vue + +``` + +### 2. Wrapping a component from UI library in a new component, such as `el-button` from `element-plus`. + +- Option Api ```tsx import { defineComponent, type AttrsType } from 'vue'; @@ -165,6 +203,14 @@ const MyComp = defineComponent({ }} />; ``` +- Setup Script +```vue +// MyButton.vue + +``` # Unresolved questions Naming suggestions or improvements on the API are welcome.