惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
V
V2EX
G
Google Developers Blog
F
Full Disclosure
Martin Fowler
Martin Fowler
宝玉的分享
宝玉的分享
H
Hacker News: Front Page
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
NISL@THU
NISL@THU
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
Hacker News - Newest:
Hacker News - Newest: "LLM"
A
About on SuperTechFans
The Cloudflare Blog
C
Cisco Blogs
D
DataBreaches.Net
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Vercel News
Vercel News
P
Privacy International News Feed
Microsoft Security Blog
Microsoft Security Blog
Help Net Security
Help Net Security
Recorded Future
Recorded Future
PCI Perspectives
PCI Perspectives
S
Schneier on Security
AI
AI
N
News | PayPal Newsroom
雷峰网
雷峰网
C
Cyber Attacks, Cyber Crime and Cyber Security
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
L
LINUX DO - 最新话题
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
Schneier on Security
Schneier on Security
S
Securelist
云风的 BLOG
云风的 BLOG
Stack Overflow Blog
Stack Overflow Blog
博客园_首页
AWS News Blog
AWS News Blog
TaoSecurity Blog
TaoSecurity Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园 - 三生石上(FineUI控件)
C
CXSECURITY Database RSS Feed - CXSecurity.com
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cloudbric
Cloudbric
C
Cybersecurity and Infrastructure Security Agency CISA
Project Zero
Project Zero
C
Check Point Blog
S
Security Affairs

Anthony Fu

Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony's Roads to Open Source - The Set Theory (React ver.) Mental Health in Open Source The Evolution of Shiki v1.0 The Magic in Shiki Magic Move Anthony's Roads to Open Source - The Progressive Path Anthony Fu Anthony Fu Anthony Fu Anthony's Roads to Open Source - The Set Theory Now, and the Future of Nuxt Devtools Anthony's Roads to Open Source - The Set Theory Anthony Fu Anthony Fu Stable Diffusion QR Code 101 Refining AI Generated QR Code Stylistic QR Code with Stable Diffusion Anthony Fu How I Manage GitHub Notifications Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Dev SSR on Nuxt with Vite Why I don't use Prettier Anthony Fu Anthony Fu Ship ESM & CJS in one Package Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Reflection of Speaking in Public Anthony Fu Windi CSS and Tailwind JIT Typed Provide and Inject in Vue Color Scheme for VS Code Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Destructuring... with object or array? Anthony Fu Anthony Fu Make Libraries Working with Vue 2 and 3 Anthony Fu Anthony Fu Anthony Fu
Anthony Fu
Anthony Fu · 2020-06-28 · via Anthony Fu
  • Type for this
    • ThisType

As you may or may not know, I am working on preparing to release the v1.0 version for @vue/composition-api recently. One of the current problems is that the type inference does not play well #338. So I get a chance to have a deeper look at vue-next’s type implementations. I will tell you what I learned and how magic works in Vue.

Forget about the setup() function and Composition API for now, let talk about the options API in Vue 2 that everybody familiar with. In a classical example, we would have data, computed, methods and some other fields like this:

export default {
  data: {
    first_name: 'Anthony',
    last_name: 'Fu',
  },
  computed: {
    full_name() {
      return `${this.first_name} ${this.last_name}`
    },
  },
  methods: {
    hi() {
      alert(this.full_name)
    }
  }
}

It works well in JavaScript and putting all the context into this is pretty straightforward and easy to understand. But when you switch to TypeScript for static type checking. this will not be the context you expected. How can we make the types work for Vue like the example above?

Type for this

To explicitly assign the type to this, we can simply use the this parameter:

interface Context {
  $injected: string
}

function bar(this: Context, a: number) {
  this.$injected // ok
}

The limitation of this approach is that we will lose the signature of the method when working with a dict of methods:

type Methods = Record<string, (this: Context, ...args: any[]) => any>

const methods: Methods = {
  bar(a: number) {
    this.$injected // ok
  }
}

methods.bar('foo', 'bar') // no error, the type of arguments becomes `any[]`

We would not want to ask users to explicitly type this in every method in order to make the type checking works. So we will need another approach.

ThisType

After digging into Vue’s code, I found an interesting TypeScirpt utility ThisType. The official doc says:

This utility does not return a transformed type. Instead, it serves as a marker for a contextual this type.

ThisType would affect all the nested functions. With it, we can have:

interface Methods {
  double: (a: number) => number
  deep: {
    nested: {
      half: (a: number) => number
    }
  }
}

const methods: Methods & ThisType<Methods & Context> = {
  double(a: number) {
    this.$injected // ok
    return a * 2
  },
  deep: {
    nested: {
      half(a: number) {
        this.$injected // ok
        return a / 2
      }
    }
  }
}

methods.double(2) // ok
methods.double('foo') // error
methods.deep.nested.half(4) // ok

The typing works well, but it still requires users to define the type interface of Methods first. Can we make it infer itself automatically?

We can do that with function inference:

type Options<T> = {
  methods?: T
} & ThisType<T & Context>

function define<T>(options: Options<T>) {
  return options
}

define({
  methods: {
    foo() {
      this.$injected // ok
    },
  },
})

There is only one step left, to make context object dynamic inference from data and computed.

The full working demo would be:

/* ---- Type ---- */
export type ExtractComputedReturns<T extends any> = {
  [key in keyof T]: T[key] extends (...args: any[]) => infer TReturn
    ? TReturn
    : never
}

type Options<D = {}, C = {}, M = {}> = {
  data: () => D
  computed: C
  methods: M
  mounted: () => void
  // and other options
}
& ThisType<D & M & ExtractComputedReturns<C>> // merge them together

function define<D, C, M>(options: Options<D, C, M>) {}

/* ---- Usage ---- */
define({
  data() {
    return {
      first_name: 'Anthony',
      last_name: 'Fu',
    }
  },
  computed: {
    fullname() {
      return `${this.first_name} ${this.last_name}`
    },
  },
  methods: {
    notify(msg: string) {
      alert(msg)
    }
  },
  mounted() {
    this.notify(this.fullname)
  },
})