You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: active-rfcs/0000-function-api.md
+99-1Lines changed: 99 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -107,7 +107,7 @@ See also:
107
107
108
108
One of the major goals of 3.0 is to provide better built-in TypeScript type inference support. Originally we tried to address this problem with the now-abandoned [Class API RFC](https://github.com/vuejs/rfcs/pull/17), but after discussion and prototyping we discovered that using Classes [doesn't fully address the typing issue](#type-issues-with-class-api).
109
109
110
-
The function-based APIs, on the other hand, are naturally type-friendly. In the prototype we have already achieved full typing support for the proposed APIs. The best part is - code written in TypeScript will look almost identical to code written in plain JavaScript. There are no manual type hints needed except for dependency injection.
110
+
The function-based APIs, on the other hand, are naturally type-friendly. In the prototype we have already achieved full typing support for the proposed APIs. The best part is - code written in TypeScript will look almost identical to code written in plain JavaScript. More details will be discussed later in this RFC.
111
111
112
112
See also:
113
113
@@ -507,6 +507,104 @@ const Descendent = {
507
507
508
508
If provided key contains a value wrapper, `inject` will also return a value wrapper and the binding will be reactive (i.e. the child will update if ancestor mutates the provided value).
509
509
510
+
## Type Inference
511
+
512
+
To get proper type inference in TypeScript, we do need to wrap a component definition in a function call:
513
+
514
+
```ts
515
+
import { createComponent } from'vue'
516
+
517
+
const MyComponent =createComponent({
518
+
props: {
519
+
msg: String
520
+
},
521
+
setup(props) {
522
+
watch(() =>props.msg, msg=> { /* ... */ })
523
+
return {
524
+
count: value(0)
525
+
}
526
+
},
527
+
render({ state, props }) {
528
+
// state typing inferred from value returned by setup()
529
+
console.log(state.count)
530
+
// props typing inferred from `props` declaration
531
+
console.log(props.msg)
532
+
533
+
// `this` exposes both state and props
534
+
console.log(this.count)
535
+
console.log(this.msg)
536
+
}
537
+
})
538
+
```
539
+
540
+
`createComponent` is conceptually similar to 2.x's `Vue.extend`, but internally it's just a no-op for typing purposes. The returned component is the object itself, but typed in a way that would provide props inference when used in TSX expressions.
541
+
542
+
If you are using Single-File Components, Vetur can implicitly add the wrapper function for you.
543
+
544
+
### Required Props
545
+
546
+
By default, props are inferred as optional properties. `required: true` will be respected if present:
547
+
548
+
```ts
549
+
import { createComponent } from'vue'
550
+
551
+
createComponent({
552
+
props: {
553
+
foo: {
554
+
type: String,
555
+
required: true
556
+
},
557
+
'bar: {
558
+
type: String
559
+
}
560
+
} asconst,
561
+
setup(props) {
562
+
props.foo// string
563
+
props.bar// string | undefined
564
+
}
565
+
})
566
+
```
567
+
568
+
Note that we need to add `as const` after the `props` declaration. This is because without `as const` the type will be `required: boolean` and won't qualify for `extends true` in conditional type operations.
569
+
570
+
> Side note: should we consider making props required by default (And can be made optional with `optional: true`)?
571
+
572
+
### Complex Prop Types
573
+
574
+
The exposed `PropType` type can be used to declare complex prop types - but it requires a force-cast via `as any`:
575
+
576
+
```ts
577
+
import { createComponent, PropType } from'vue'
578
+
579
+
createComponent({
580
+
props: {
581
+
options: {
582
+
type: (nullasany) asPropType<{ msg:string }>,
583
+
}
584
+
},
585
+
setup(props) {
586
+
props.options// { msg: string } | undefined
587
+
}
588
+
})
589
+
```
590
+
591
+
### Dependency Injection Typing
592
+
593
+
The `inject` method is the only API that requires manual typing:
594
+
595
+
```ts
596
+
import { createComponent, inject, Value } from'vue'
597
+
598
+
createComponent({
599
+
setup() {
600
+
const count:Value<number> =inject(CountSymbol)
601
+
return {
602
+
count
603
+
}
604
+
}
605
+
})
606
+
```
607
+
510
608
# Drawbacks
511
609
512
610
- Makes it more difficult to reflect and manipulate component definitions.
0 commit comments