Skip to content

Types of computed properties seem to be ignored, resulting in run-time errors instead of compile-time errors #11254

Open
@Threebow

Description

@Threebow

Vue version

3.4.31

Link to minimal reproduction

https://play.vuejs.org/#__SSR__eNqFVMtu2zAQ/JUte0gM+IE0PRmOgT4MtD0kQZq2FwI1La0lphJJkKs4gat/75KSHScI3JNNcnd2ZjjUVnxwbnzfoJiKWci8dgQBqXFQKVNcSEFBirk0unbWE2whs7VrCHNoYe1tDSfceyKNNPToEH4E9J9vr+ACttIA6HwKgbw2RVwZVePTuo1Nkwlce7uqsIa3Z1OgEsF5W3hVp0G6wgAbTSUYC+i99UPIMThNCApqHWpFWQkrpA1iB5cwCjTodQaJkxQ9KylAmTwVeJboze7cNFUlxViazJpAPIsueSd2sZCd4FmPMj89HcDFvBPY4yQA0w6ipA6jVo8rPIICf1PT/8Bmk+5O+AZ4QVi7ShHyCmD2ZjSKv/Dk4LspqMDGeAxNRcOkdIWV3cDyQNNY50tQWYYhwFrpijsIfGNI1zjsEGFTogHNUShtU+WpLFb1dwKp9HAW2HWadjhnuRfdg9ZIpc17mZyBpBRGo06Om1+mfGy3hxfAZKFtZxOXHHhV9fkUls/sXoIOPNt7zKh6TJecD6MarmI/mCQXLuGUw9AzUxk1quJabkykNGejjCgq4GAIX65+LX4ubqKjvJmCCNZwvWP5aOg1C2PUIpxl21RTlPTSwAEHDl7IB75CNuCZnHF8N3sTZpODFIghv08O3FoX47tgDT/ilCQp+kH+ypHmQErBuB0xKVip3XxLe+SbHWHuKTH788r+XXiIe1JcR7X+HqXYn5HyBVJ3vPh+iQ/8f39Y27ypuPrI4Q0GWzWRY1f2sTE50z6oS2y/ps8PZ+Y2LB4ITdiJikRjZZvqpeCv0acj0p/ono/fpz5+aezi73v0EZMN5IPx+Zlo/wEBe7jj

Steps to reproduce

Define a computed property with an explicit generic, and a return value that does not match the type of the generic. Additionally, define a computed property that may return null. Like so:

<script setup lang="ts">
    import { computed } from 'vue'
    
    type UserDTO = {
      id: string
      name: string
    }
    
    const notNullUser = computed<UserDTO>(() => {
      return null // Problem #1: the generic of the computed method is defined as `UserDTO`, that type does NOT include `null`, therefore, this should not compile
    })
    
    // Setup for Problem #2
    const maybeNullUser = computed<UserDTO | null>(() => {
      return null
    })
</script>

Then, use the computed properties in your template, as such:

<template>
  <p>Name: {{ notNullUser.id }}</p> <!-- Problem #1: this is technically expected, as `computed` type is `UserDTO` and cannot be null, however, for some reason the compiler does not detect the method ACTUALLY returning `null`, and as a result we get this error at runtime -->
  <p>Name 2: {{ maybeNullUser.name }}</p> <!-- Problem #2: `maybeNullUser` is typed as `UserDTO | null`, therefore, this property access should not compile as `maybeNullUser` can be null (and, in this example, is) -->
</template>

What is expected?

I expect to receive two compile-time errors:

  1. The first computed call should fail compilation, telling us that null is not assignable to the UserDTO type passed as a generic to the computed method.
  2. The access of maybeNullUser.name in the second p tag should should result in an error saying something along the lines of user is potentially null, and demanding that access to maybeNullUser.name be locked behind a v-if="maybeNullUser" check before being accessed (i.e., the most basic type narrowing)

What is actually happening?

The component compiled without failure, and rendering the component results in an error at runtime: Cannot read properties of null (reading 'id'). This is caused by the access of notNullUser.id in the p tag in the reproduction.

I also presume that the same error would happen as a result of accessing the second p tag, the maybeNullUser.name accessor should fail, as maybeNullUser's type dictates that it can be null.

This behavior occurs regardless of SSR, but I have enabled SSR in the reproduction link because that is what I use in my projects. That being said, I do not believe that this is an issue with SSR itself.

System Info

I made this reproduction on the Vue SFC playground, and shared a link. As a result, I am not sure how relevant the result of npx envinfo is, when ran in an unrelated environment.

Any additional comments?

I noticed that type narrowing of discriminated unions does not work within templates. I decided to investigate further, and found that this reproduction is a lot less complex, and fails in a similar way. This leads me to believe that there is an overall lack of support for type information within the template, despite Vue documentation saying TypeScript is indeed supported within templates.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions