TypeScript with Composition API | view.js (2023)

jobs throughwww.vuejobs.com

This page assumes that you have already read the overviewUsing Vue with TypeScript.

Accessories for writing components#

Use<script setting> #

if you use<script setting>, DiedefineAccesorios()The macro supports inferring accessory types based on its argument:


<Text setting language="t">ConstantlyAccesories= defineaccessories({ Foo: { writes:line, necessary: true }, Bar:number})Accesories.Foo// StringAccesories.Bar// number | not defined</Text>

This is called a "runtime declaration" because the argument passed todefineAccesorios()is used as runtimeAccesoriesPossibility.

However, it is often easier to define purely typed props via a generic type argument:


<Text setting language="t">ConstantlyAccesories= defineaccessories<{ Foo: line Bar?: number}>()</Text>

This is called a "type-based declaration". The compiler tries its best to infer the equivalent run-time options based on the type argument. In this case, our second example compiles exactly the same runtime options as the first example.

You can use either a type-based declaration OR a run-time declaration, but you cannot use both at the same time.

We can also move the accessory types to a separate interface:


<Text setting language="t">interface Accesories { Foo: line Bar?: number}ConstantlyAccesories= defineaccessories<Accesories>()</Text>

syntax restrictions#

To generate the correct runtime code, the generic argument todefineAccesorios()must be one of the following:

  • A literal object type:


    defineaccessories<{ /*... */ }>()
  • A reference to an interface or object type literalin the same file:


    interface Accesories {/* ... */}defineaccessories<Accesories>()

The object or interface type literal may contain references to types imported from other files, but the generic argument itself was passeddefineaccessories can notbe an imported type:


import { Accesories } since './other-file'// Unsupporteddefineaccessories<Accesories>()

This is because Vue components are compiled in isolation and the compiler does not currently crawl imported files to analyze the source type. This limitation may be removed in a future release.

Default settings for accessories#

If we use a type-based declaration, we lose the ability to declare default values ​​for props. This can be solved by thewith default valuesCompiler Macro:


export interface Accesories { message?: line hang tags?: line[]}ConstantlyAccesories= with default values(defineaccessories<Accesories>(), { message: 'Hallo', hang tags: () =>['a', 'Von']})

This is compiled into equivalent runtime propsLackOptions additionally thewith default valueshelper provides type checks for default values ​​and ensures that values ​​are returnedAccesoriestype removed the optional flags for properties that declared default values.

Alternatively, you can use the currently experimental onereactivity transformation:


<Text setting language="t">interface Accesories { Name: line tell?: number}// reactive structure for defineProps()// The default value is compiled into the appropriate runtime optionConstantly {Name,tell= 100 } = defineaccessories<Accesories>()</Text>

This behavior currently requiresexpress subscription.

sin<script setting> #

Nobody<script setting>, it is necessary to usedefinirComponente()to allow conclusions about accessory types. The type of Props object to pass toSetting()is derived from theAccesoriesPossibility.


import { definirComponente } since 'outlook'export Lack definirComponente({ Accesories: { message:line }, setting(Accesories) { Accesories.message // <-- type: string }})

Write component issues#

In<script setting>, DieoutputThe function can also be written using the runtime declaration OR the type declaration:


<Text setting language="t">// execution timeConstantlyoutput= Define emissions(['change', 'To update'])// based on typesConstantlyoutput= Define emissions<{ (mi: 'change', I WOULD: number): file (mi: 'To update', bravery: line): file}>()</Text>

The type argument must be a type literal withcall signatures. The literal type is used as the return type.outputFunction. As we can see, the type declaration gives us much finer control over the type constraints of the events emitted.

when not in use<script setting>,definirComponente()is able to derive the events allowed foroutputFeature exposed in configuration context:


import { definirComponente } since 'outlook'export Lack definirComponente({ output:['change'], setting(Accesories, { output }) { output('change')// <-- Type checking / auto-completion }})

typewritingReferee() #

References infer the type from the initial value:


import { referee } since 'outlook'// Derived type: Ref<number>ConstantlyYear= referee(2020)// => TS error: type 'string' cannot be mapped to type 'number'.Year.bravery= '2020'

Sometimes we may need to specify complex types for the intrinsic value of a reference. We can do that with therefereewrites:


import { referee } since 'outlook'import writes { referee } since 'outlook'ConstantlyYear: referee<line | number> = referee('2020')Year.bravery= 2020 // OK!

Or by passing a generic argument when callingReferee()to override the default inference:


// Result type: Ref<string | number>ConstantlyYear= referee<line | number>('2020')Year.bravery= 2020 // OK!

If you supply a generic type argument but omit the initial value, the resulting type is an enclosing union typeNot defined:


// Derived type: Ref<number | undefined>ConstantlyNorte= referee<number>()

typewritingReagent() #

Reagent()it also implicitly infers the nature of its argument:


import { Reagent } since 'outlook'// derived type: {title: string}ConstantlyBook= Reagent({ title: 'See 3 guide' })

To explicitly write aReagentproperty we can use interfaces:


import { Reagent } since 'outlook'interface Buch { title: line Year?: number}ConstantlyBook: Buch = Reagent({ title: 'See 3 guide' })


It is not recommended to use the generic argument ofReagent()since the return type, which handles unpacking of nested references, is different from the generic argument type.

typewritingcalculated() #

calculated()infers its type based on the return value of the getter:


import { referee, calculated } since 'outlook'Constantlytell= referee(0)// Derived type: ComputedRef<number>Constantlydouble= calculated(() =>tell.bravery* 2)// => TS error: property 'split' does not exist on type 'number'Constantlyresult=double.bravery.pull apart('')

You can also specify an explicit type via a generic argument:


Constantlydouble= calculated<number>(() => { // typo if this doesn't return a number})

Write event handlers#

When dealing with native DOM events, it can be helpful to properly spell the argument we're passing to the controller. Let's look at this example:


<Text setting language="t">function handleChange(event) { // `event` is implicitly of `any` type console.log in(event.Goal.bravery)}</Text><model> <Entry writes="Text"@change="handleChange"/></model>

Without type annotation, theeventArgument implicitly has a type ofnone. This will also result in a TS error if"strict": trueÖ"noImplicitAny": truebe used in ittsconfig.json. Therefore, it is recommended to comment out the event handler argument explicitly. Also, you may need to explicitly cast the properties toevent:


function handleChange(event: incident) { console.log in((event.Goal like HTMLInputElement).bravery)}

Enter Deploy/Inject#

Feeding and injecting usually take place in separate components. In order to correctly write the fed values, Vue provides ainjection keyInterface that is a generic type that is extendedSymbol. It can be used to synchronize the type of value inserted between the provider and the consumer:


import { Offer, inject } since 'outlook'import writes { injection key } since 'outlook'Constantlykey= Symbol()like injection key<line>Offer(Key, 'Foo')// Specifying a non-string value will result in an errorConstantlyFoo= inject(Key)// type of foo: string | not defined

It is recommended to put the injection key in a separate file so that it can be imported into multiple components.

When using string injection keys, is the type of the injected valuea foreign, and must be declared explicitly via a generic type argument:


ConstantlyFoo= inject<line>('Foo')// type: string | not defined

Note that the value fed in can still beNot defined, since there is no guarantee that a provider will provide this value at runtime.

ThatNot definedThe type can be removed by providing a default value:


ConstantlyFoo= inject<line>('Foo', 'Bar')// type: string

If you are sure that the value will always be provided, you can also force the value to be converted:


ConstantlyFoo= inject('Foo')like line

Write template references#

Template references must be created with an explicit generic type argument and an initial value ofNull:


<Text setting language="t">import { referee, in assembled condition } since 'outlook'ConstantlyDie= referee<HTMLInputElement | Null>(Null)in assembled condition(() => { Die.bravery?.Focus()})</Text><model> <Entry referee="Die"/></model>

Note that strong type safety requires using optional concatenation or type guards when accessingthe value. This is because is the initial reference valueNulluntil the component is assembled, and can also be adjustedNullwhen the referenced element is unmounted byinsi.

Write references to component templates#

Sometimes you may need to annotate a template reference for a child component to invoke its public method. For example, we have oneMiModalchild component with a method that opens the modal:


<!-- MiModal.view --><Text setting language="t">import { referee } since 'outlook'Constantlycontent is displayed= referee(NOT CORRECT)Constantlyopen= () =>(Content is displayed.bravery= true)definirExponer({open})</Text>

To get the instance type ofMiModal, we need to get its type via firstSo'ne Art, then use the built-in TypeScriptinstance typeInstance type extract utility:


<!-- app.vue --><Text setting language="t">importMiModalsince './MiModal.vue'Constantlymodal= referee<instance type< somehowMiModal> | Null>(Null)ConstantlyopenModal= () => { modal.bravery?.open()}</Text>

Note that if you want to use this technique for TypeScript files instead of Vue SFC, you need to enable Volaracquisition mode.


Top Articles
Latest Posts
Article information

Author: Horacio Brakus JD

Last Updated: 08/28/2023

Views: 5527

Rating: 4 / 5 (71 voted)

Reviews: 86% of readers found this page helpful

Author information

Name: Horacio Brakus JD

Birthday: 1999-08-21

Address: Apt. 524 43384 Minnie Prairie, South Edda, MA 62804

Phone: +5931039998219

Job: Sales Strategist

Hobby: Sculling, Kitesurfing, Orienteering, Painting, Computer programming, Creative writing, Scuba diving

Introduction: My name is Horacio Brakus JD, I am a lively, splendid, jolly, vivacious, vast, cheerful, agreeable person who loves writing and wants to share my knowledge and understanding with you.