欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue?props使用typescript自定義類型的方法實(shí)例

 更新時(shí)間:2023年01月28日 14:09:22   作者:w55100  
這篇文章主要給大家介紹了關(guān)于vue?props使用typescript自定義類型的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

前言

Base: vue@3.2.33 + typescript@4.1.6 + npm@8.5.0

嘗試解決將ts中自定義的interface/type,傳vue的props屬性的問題。

記錄一下過程和思路。

一、問題定位

官方文檔中說,props自定義類型檢查是可能的。

In addition, type can also be a custom class or constructor function and the assertion will be made with an instanceof check. For example, given the following class:
https://vuejs.org/guide/components/props.html#boolean-casting

但注意到,文檔給出的支持類型為class或者constructor function。

官方給的example也是基于class的

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}

export default {
  props: {
    author: Person
  }
}

但如果使用ts中的interface,就會(huì)報(bào)錯(cuò)'Person' only refers to a type, but is being used as a value here.

見下面代碼:

interface Person {
  firstName: string
  lastName: string
}

//錯(cuò)誤用法
export default {
  props: {
    author: Person
  }
}

歸根結(jié)底是vue源碼里的定義方式

declare interface PropOptions<T = any, D = T> {
    type?: PropType<T> | true | null;
    required?: boolean;
    default?: D | DefaultFactory<D> | null | undefined | object;
    validator?(value: unknown): boolean;
}

export declare type PropType<T> = PropConstructor<T> | PropConstructor<T>[];

declare type PropConstructor<T = any> = {
    new (...args: any[]): T & {};
} | {
    (): T;
} | PropMethod<T>;

declare type PropMethod<T, TConstructor = any> = [T] extends [
((...args: any) => any) | undefined
] ? {
    new (): TConstructor;
    (): T;
    readonly prototype: TConstructor;
} : never;

對(duì)type屬性的檢查是PropType<T>

一路查找定義到PropConstructor<T>

可以看到PropConstructor的定義

{ new ( ..args: any[]): T & {} } ,class類型。擁有“接受任何參數(shù)并返回指定類型的構(gòu)造函數(shù)”的一個(gè)class。{ (): T } 函數(shù)類型,一個(gè)無參傳入并返回指定類型的函數(shù)。PropMethod<T>類型。

其中, PropMethod<T> 類型使用了extends條件類型,用于包裹T為任意函數(shù)類型or undefined的情況,此時(shí)會(huì)需要一個(gè)含有構(gòu)造函數(shù)的函數(shù)類型,這個(gè)函數(shù)類型的實(shí)例在調(diào)用時(shí)會(huì)返回T。

總結(jié)一下,PropConstructor<T>的要求無非就是

要么你是class,構(gòu)造函數(shù)返回T,

要么傳入一個(gè)無參函數(shù)返回T。

要么把這個(gè)無參函數(shù)包裹在一個(gè)有構(gòu)造函數(shù)的class里(用PropMethod泛型)

二、初級(jí)解決方案

上面部分我們已經(jīng)定位到了問題。

我們本質(zhì)上是要通過PropType的校驗(yàn)。

解法一,函數(shù)法

參照PropConstructor的要求寫就可以了。

用一個(gè) ()=>T 類型的函數(shù)為type賦值,通過校驗(yàn)。

interface Person {
  firstName: string
  lastName: string
}

const PersonTypeHelper = function (): Person {
  return {} as Person
}

export default {
  props: {
    author: {
      type: PersonTypeHelper
    }
  }
}

上面這個(gè)思路可以繼續(xù)優(yōu)化

//更簡單一點(diǎn)不寫實(shí)現(xiàn)
declare var PersonType : ()=> Person

export default {
  props: {
    author: {
      type: PersonType 
    }
  }
}

這樣仍然很麻煩,不能為每個(gè)自定義interface 都寫一個(gè)func / var吧。

我們可以繼續(xù)優(yōu)化,寫一個(gè)更通用的var。

declare var commonType: ()=> any

export default {
  props: {
    author: {
      //使用時(shí)把Person修改成其他自定義類型即可
      type: commonType as ()=> Person  
    }
  }
}

這樣寫能過ide的類型推導(dǎo),但是run起來會(huì)報(bào)錯(cuò) ReferenceError: PersonType is not defined
當(dāng)然這并不重要。因?yàn)榫退銓懥藢?shí)現(xiàn)也不會(huì)通過類型校驗(yàn)。
我會(huì)在下個(gè)章節(jié)解決類型校驗(yàn)問題,此處先展示思路。

解法二 PropType泛型

上面這個(gè)思路,需要每次聲明一個(gè)commonType變量。

對(duì)某些庫函數(shù)黨來說,可能不太舒服。

既然思路是繞過PropType的校驗(yàn),直接使用PropType不是最直觀的嗎?

interface Person {
  firstName: string
  lastName: string
}

import type { PropType } from "vue"

export default {
  props: {
    author: {
      type: Object as PropType<Person> 
      // type: {} as PropType<Person>
    }
  }
}

這樣做其實(shí)跟使用commonType沒啥本質(zhì)區(qū)別。

因?yàn)閕mport type {PropType}也需要一行。

聲明一個(gè)commonType也需要一行。

我們其實(shí)也可以使用PropMethod泛型,但是由于vue沒有做直接export,日常使用不太方便。

上面兩個(gè)解法都能過ide類型推導(dǎo),但是runtime時(shí)有點(diǎn)問題。

三、props的校驗(yàn)過程

前面的解法,在運(yùn)行過程中可能會(huì)報(bào)警告,類型校驗(yàn)不通過。
[Vue warn]: Invalid prop: type check failed for prop "formItems". Expected , got Array

為了解決這個(gè)警告。

我們來讀一下校驗(yàn)類型的源碼。

傳入的value是實(shí)際獲得的對(duì)象,type是我們?cè)?{type: Person}里傳入的值。

function assertType(value, type) {
    let valid;
    const expectedType = getType(type);
    if (isSimpleType(expectedType)) {
        const t = typeof value;
        valid = t === expectedType.toLowerCase();
        // for primitive wrapper objects
        if (!valid && t === 'object') {
            valid = value instanceof type;
        }
    }
    else if (expectedType === 'Object') {
        valid = isObject(value);
    }
    else if (expectedType === 'Array') {
        valid = isArray(value);
    }
    else if (expectedType === 'null') {
        valid = value === null;
    }
    else {
        valid = value instanceof type;
    }
    return {
        valid,
        expectedType
    };
}

const isSimpleType = /*#__PURE__*/ makeMap('String,Number,Boolean,Function,Symbol,BigInt');

// use function string name to check type constructors
// so that it works across vms / iframes.
function getType(ctor) {
    const match = ctor && ctor.toString().match(/^\s*function (\w+)/);
    return match ? match[1] : ctor === null ? 'null' : '';
}

前面的代碼就是區(qū)分簡單數(shù)據(jù)類型,對(duì)象、數(shù)組,判空。

最后一個(gè)是,如果type不在上述內(nèi)置類型中,就使用 value instanceof type 來判斷。

這里的關(guān)鍵在于getType函數(shù)。

vue的getType函數(shù),假定我們傳入的是一個(gè)constructor,

假定我們的constructor寫法是

function ctorName(){ ...}	

試圖捕獲這個(gè)ctorName。

如果不這么寫, 返回的expectedType名稱就是空字符串。

這解釋了,如果我們僅僅讀了文檔,老老實(shí)實(shí)地按文檔說的,傳入了一個(gè)構(gòu)造函數(shù),會(huì)報(bào)錯(cuò)。

class Person{
	constructor (){...}
}

props:{
	type: Person.constructor
}

因?yàn)閭魅?Person.constructor在vue內(nèi)部會(huì)被解析成getType函數(shù)匹配為 ‘Function’ 類型。

構(gòu)造函數(shù)沒有名字,默認(rèn)名字是function Function(){ somecode}

于是發(fā)生類型不匹配

TS2769: No overload matches this call.
  The last overload gave the following error.
    Type '{ type: Function; }' is not assignable to type 'Prop<unknown, unknown> | null'.
      Types of property 'type' are incompatible.
        Type 'Function' is not assignable to type 'true | PropType<unknown> | null | undefined'.
          Type 'Function' is missing the following properties from type 'PropConstructor<unknown>[]': pop, push, concat, join, and 28 more.

當(dāng)然這個(gè)不重要,重要的是 valid = value instanceof type 這句。

instanceof 關(guān)鍵詞的原理是不斷回溯左值的__proto__,直到其找到一個(gè)等于 右值.prototype的原型,就返回true。遍歷到根 null就返回false。

function instanceof(L, R) {
  // L為左值,R為右值
  var R = R.prototype
  var L = L.__proto__
  while (true) {
    if (L === null) {
      return false
    }
    if (L === R) {
      return true
    }
    L = L.__proto__
  }
}

在上述例子中,校驗(yàn)失敗的核心原因在于。

如果我們傳入一個(gè)匿名函數(shù) ()=> T 作為type右值。

由于匿名函數(shù)是沒有prototype的,

所以傳入任意value instanceof anonymous 會(huì)返回false。

如果我們采用PropType<T>來寫也有問題,

{
	type: Object as PropType<Person> 
}

右值中的類型會(huì)變成Object。

于是在instanceof比較的時(shí)候 , R.prototype === Object.prototype。

因此傳入任意非空數(shù)據(jù)都會(huì)通過vue的校驗(yàn)。

因?yàn)槿我夥强諗?shù)據(jù)遍歷 __proto__都會(huì)來到Object.prototype。

所以上面兩個(gè)解法,其實(shí)本質(zhì)上是,

無論傳入什么都會(huì)報(bào)警告和無論傳入什么都不報(bào)警告的區(qū)別。

四、后話

我試了另外幾種思路,由于interface只在編譯時(shí)起作用,本身并不能設(shè)置prototype。

所以無論怎么折騰interface / type 的泛型,都不太好解決這個(gè)問題。

目前來看的一種折中方式是使用class。

class有prototype。

但是需要嚴(yán)格地約定,在傳入端也使用class的constructor構(gòu)造數(shù)據(jù)。

這樣才能將原型存進(jìn)數(shù)據(jù)里。

但是這種做法對(duì)一些小型接口其實(shí)并不友好。

例如

export interface Person {
  name: string
  age: number
}

用class的話就非得改成

export class Person  {
  name: string
  age: number
  constructor(n,a) {
    this.name = n
    this.age = a
  }
}

然后在傳入的地方使用構(gòu)造函數(shù)。

import {Person} from "./types"
const data = new Person("aa",123)

這顯然不如我們直接使用對(duì)象字面量方便。

const data = {name:"aa", age:123}

這歸根結(jié)底是因?yàn)?,instanceof是基于原型校驗(yàn),而非值校驗(yàn)的。

使用對(duì)象字面量并不能通過校驗(yàn)。

當(dāng)然了,vue其實(shí)是支持我們寫一個(gè)自定義的validator()的。

上面的探索只是想嘗試?yán)@過這個(gè)步驟。

custom validator相關(guān)源碼。

function validateProp(name, value, prop, isAbsent) {
    const { type, required, validator } = prop;
    // required!
    if (required && isAbsent) {
        warn('Missing required prop: "' + name + '"');
        return;
    }
    // missing but optional
    if (value == null && !prop.required) {
        return;
    }
    // type check
    if (type != null && type !== true) {
        let isValid = false;
        const types = isArray(type) ? type : [type];
        const expectedTypes = [];
        // value is valid as long as one of the specified types match
        for (let i = 0; i < types.length && !isValid; i++) {
            const { valid, expectedType } = assertType(value, types[i]);
            expectedTypes.push(expectedType || '');
            isValid = valid;
        }
        if (!isValid) {
            warn(getInvalidTypeMessage(name, value, expectedTypes));
            return;
        }
    }
    // custom validator
    if (validator && !validator(value)) {
        warn('Invalid prop: custom validator check failed for prop "' + name + '".');
    }
}

需要注意的是,vue這里做了double check。

如果有傳入type,先在type做一次校驗(yàn)。

通過type校驗(yàn)后,再看有沒有validator,如果有額外做一次。

這提示我們,

對(duì)于復(fù)雜的自定義數(shù)據(jù)類型,

type本身不能成為校驗(yàn)工具,最好不寫,減少一次運(yùn)算。

export default {
  props: {
    author: {
      validator: function (value: Person) {
        return typeof value.name === "string" && typeof value.age === "number"
      }
    }
  }
}

參考

  • //https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#inferring-within-conditional-types
  • //https://frontendsociety.com/using-a-typescript-interfaces-and-types-as-a-prop-type-in-vuejs-508ab3f83480
  • //https://blog.csdn.net/qq_34998786/article/details/120300361

總結(jié) 

到此這篇關(guān)于vue props使用typescript自定義類型的文章就介紹到這了,更多相關(guān)vue props使用ts自定義類型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue項(xiàng)目如何設(shè)置反向代理和cookie設(shè)置問題

    Vue項(xiàng)目如何設(shè)置反向代理和cookie設(shè)置問題

    這篇文章主要介紹了Vue項(xiàng)目如何設(shè)置反向代理和cookie設(shè)置問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vue+Element實(shí)現(xiàn)表格單元格編輯

    Vue+Element實(shí)現(xiàn)表格單元格編輯

    這篇文章主要為大家詳細(xì)介紹了Vue+Element實(shí)現(xiàn)表格單元格編輯,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Vue使用視頻作為網(wǎng)頁背景的實(shí)現(xiàn)指南

    Vue使用視頻作為網(wǎng)頁背景的實(shí)現(xiàn)指南

    在現(xiàn)代網(wǎng)頁設(shè)計(jì)中,視頻背景逐漸成為一種流行的設(shè)計(jì)趨勢,它不僅能夠提升網(wǎng)頁的動(dòng)態(tài)效果,還可以在視覺上抓住用戶的注意力,本文將詳細(xì)講解如何在頁面中使用視頻作為背景,并確保內(nèi)容可見、頁面元素布局合理,需要的朋友可以參考下
    2024-10-10
  • vue vant Area組件使用詳解

    vue vant Area組件使用詳解

    這篇文章主要介紹了vue vant Area組件使用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • Vue?Router解決多路由復(fù)用同一組件頁面不刷新問題(場景分析)

    Vue?Router解決多路由復(fù)用同一組件頁面不刷新問題(場景分析)

    這篇文章主要介紹了Vue?Router解決多路由復(fù)用同一組件頁面不刷新問題,多路由復(fù)用同一組件的場景分析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • vue 使用monaco實(shí)現(xiàn)代碼高亮

    vue 使用monaco實(shí)現(xiàn)代碼高亮

    這篇文章主要介紹了vue 使用monaco實(shí)現(xiàn)代碼高亮的方法,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下
    2021-03-03
  • Vue預(yù)渲染:prerender-spa-plugin生成靜態(tài)HTML與vue-meta-info更新meta

    Vue預(yù)渲染:prerender-spa-plugin生成靜態(tài)HTML與vue-meta-info更新meta

    Vue.js中,prerender-spa-plugin和vue-meta-info插件的結(jié)合使用,提供了解決SEO問題的方案,prerender-spa-plugin通過預(yù)渲染技術(shù)生成靜態(tài)HTML,而vue-meta-info則能動(dòng)態(tài)管理頁面元數(shù)據(jù),本文將探討如何使用這兩個(gè)工具優(yōu)化Vue.js項(xiàng)目的SEO表現(xiàn),包括安裝、配置及注意事項(xiàng)
    2024-10-10
  • Vue?vant-ui使用van-uploader實(shí)現(xiàn)頭像上傳功能

    Vue?vant-ui使用van-uploader實(shí)現(xiàn)頭像上傳功能

    這篇文章主要介紹了Vue?vant-ui使用van-uploader實(shí)現(xiàn)頭像圖片上傳,項(xiàng)目中是使用有贊vant-ui框架實(shí)現(xiàn)的頭像上傳替換功能,用到了封裝的圖片壓縮封裝之后再去上傳圖片this.$imgUpload.imgZip(),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-05-05
  • 使用konva和vue-konva庫實(shí)現(xiàn)拖拽滑塊驗(yàn)證功能

    使用konva和vue-konva庫實(shí)現(xiàn)拖拽滑塊驗(yàn)證功能

    這篇文章主要介紹了使用konva和vue-konva完成前端拖拽滑塊驗(yàn)證功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • ssm+vue前后端分離框架整合實(shí)現(xiàn)(附源碼)

    ssm+vue前后端分離框架整合實(shí)現(xiàn)(附源碼)

    這篇文章主要介紹了ssm+vue前后端分離框架整合實(shí)現(xiàn)(附源碼),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07

最新評(píng)論