Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] infer v-if type narrowing on the @event #1249

Closed
pikax opened this issue Apr 29, 2022 · 1 comment
Closed

[Feature Request] infer v-if type narrowing on the @event #1249

pikax opened this issue Apr 29, 2022 · 1 comment
Labels
enhancement New feature or request

Comments

@pikax
Copy link
Member

pikax commented Apr 29, 2022

from #146

Goal

When we are writing complex templates we often have v-if that will do type narrowing on for the children, those type narrowings are really useful and is a pain to need to cast inside of the @event possibly causing bugs down the line (if a v-if is changed, etc)

// simple example
<template>
  <div v-if="test" @click="(test as number).toString()">
</template>
<script setup lang=ts>
declare const test: number | undefined;
</script>

// more complex example
<template>
  <div v-if="'a' in test" @click="(test as {a: string; aa: string}).aa.toString()"></div>
  <div v-else-if="test" @click="(test as {b: string; bb: string})..bb.toString()"></div>
</template>

<script lang="ts" setup>
let test: { a: string, aa: string } | { b: string, bb: string } | undefined;
</script>

As you can see, you need to cast it to be able to work, which is a pain, good luck inferring types correctly from third-party or nested types...

Solution

To solve this @events functions should have the same type narrowing than the render component.
This makes sense in vue because events are not fired/emitted after a component is disposed (which happens on v-if=false).

Philosophy

At first sight we can blame this to typescript, because variable can change to other types, but in vue a component is not render if the v-if condition is false, making the scope of the event pretty much the scope after the v-if (only when using prop function that might introduce a error)

Implementation

We can implement this by following this 3 rules:

  • add all the parent v-if and negate them in order if(!(BRANCH_CONDITION) return
  • If there's branches, add if(BRANCH_V_IF_CONDITION) return
  • add the @click code after the conditions

Example:

<div v-if="test" @click="test .toString()">

if(test) {
 <div onClick={$event=> {
   if(!(test)) return 
   test.toString() // safely a number here
 })
}
  <div v-if="'a' in test" @click="(test as {a: string; aa: string}).aa.toString()"></div>
  <div v-else-if="test" @click="(test as {b: string; bb: string})..bb.toString()"></div>

if (test && 'a' in test) {
    h('div', {
        onclick() {
            if (!(test && 'a' in test)) return
            test.aa.toString()
        }
    })
} else if (test) {
    h('div', {
        onClick() {
            // DO NOT NEGATE previous branches
            if ((test && 'a' in test)) return
            // NEGATE Current Branch
            if (!(test)) return;

            test.bb.toString()
        }
    })
}

playground

@johnsoncodehk johnsoncodehk added the enhancement New feature or request label Apr 29, 2022
@jesusgn90
Copy link

I'm facing this pain right now. Technically it's correct however the developer experience is sometimes bad because of this logic. #185 hopefully can unblock people wanting to customize this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants