определить компонент в Vue

внешний интерфейс исходный код TypeScript Vue.js

Автор: Цуй Цзин

Сама по себе функция defineComponent очень проста, но основная функция — протолкнуть тип под ts. Для ts-файла, если мы напишем напрямую

export default {}

В настоящее время для редактора {} — это просто тип объекта, и мы не можем специально подсказать нам, какие свойства должны быть в {} для компонента vue. Но если вы добавите слой defineComponent,

export default defineComponent({})

В это время {} становится параметром defineComponent, тогда подсказка типа параметра может реализовать подсказку атрибута в {}, а также может выполнять вывод некоторого типа и другие операции над параметром.

Но в приведенном выше примере, если вы попробуете его в файле .vue vscode, вы обнаружите, что есть также подсказка без написания defineComponent. На самом деле этим занимается плагин Vetur.

Давайте посмотрим на реализацию defineComponent.Есть 4 перегрузки.Посмотрим сначала на самую простую.Мне все равно,что здесь DefineComponent,и мы ее рассмотрим позже.

// overload 1: direct setup function
// (uses user defined props interface)
export function defineComponent<Props, RawBindings = object>(
  setup: (
    props: Readonly<Props>,
    ctx: SetupContext
  ) => RawBindings | RenderFunction
): DefineComponent<Props, RawBindings>

Параметр defineComponet — функция, функция имеет два параметра props и ctx, а тип возвращаемого значения — RawBindings или RenderFunction. Тип возвращаемого значения для defineComponent:DefineComponent<Props, RawBindings>. Среди них два общих реквизита и RawBindings. Пропсы будут определяться в соответствии с типом, переданным в первый параметр настройки, когда мы на самом деле пишем, а RawBindings будут определяться в соответствии с возвращаемым значением нашей настройки. Длинный абзац более запутан, напишите аналогичный простой пример, чтобы увидеть:

  • Простая демонстрация, похожая на использование props, выглядит следующим образом: мы передаем различные типы параметров в a, и типы возвращаемых значений define также различны. это называетсяGeneric Functions

    declare function define<Props>(a: Props): Props
    
    const arg1:string = '123'
    const result1 = define(arg1) // result1:string
    
    const arg2:number = 1
    const result2 = define(arg2) // result2: number
    
  • Простая демонстрация, похожая на RawBindings, выглядит следующим образом: тип возвращаемого значения установки отличается, и тип возвращаемого значения определения также отличается.

    declare function define<T>(setup: ()=>T): T
    
    const arg1:string = '123'
    const resul1 = define(() => {
      return arg1
    })
    
    const arg2:number = 1
    const result2 = define(() => {
      return arg2
    })
    

Из приведенных выше двух простых демонстраций мы можем понять значение перегрузки 1. Тип возвращаемого значения для defineComponent:DefineComponent<Props, RawBindings>, где Props – это первый тип параметра настройки, RawBindings – тип возвращаемого значения настройки. Если мы возвращаем значение в функцию, значением по умолчанию является объект. Из него вы можете понять основное использование вывода ts для следующего определения

declare function define<T>(a: T): T

Тип T может быть динамически определен в соответствии с параметрами, переданными во время выполнения.Этот метод также является единственной связью между типами времени выполнения и статическими типами typescript.Много раз мы хотим передать типы параметров во время выполнения, чтобы определить другие связанные типы, мы можем использовать этот метод.

Затем посмотрите на definComponent, его перегрузки 2, 3 и 4 предназначены для работы с различными типами свойств в параметрах. Посмотрите объявления самых распространенных реквизитов типа object

export function defineComponent<
  // the Readonly constraint allows TS to treat the type of { required: true }
  // as constant instead of boolean.
  PropsOptions extends Readonly<ComponentPropsOptions>,
  RawBindings,
  D,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  E extends EmitsOptions = Record<string, any>,
  EE extends string = string
>(
  options: ComponentOptionsWithObjectProps<
    PropsOptions,
    RawBindings,
    D,
    C,
    M,
    Mixin,
    Extends,
    E,
    EE
  >
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>

Подобно идее перегрузки 1 выше, основная идея состоит в том, чтобы получить различные дженерики на основе содержимого опций, записанных во время выполнения. Первый параметр настройки в vue3 — реквизит, и тип этого реквизита должен совпадать с тем, что мы передали в параметрах. это вComponentOptionsWithObjectProps中实现的。 код показывает, как показано ниже

export type ComponentOptionsWithObjectProps<
  PropsOptions = ComponentObjectPropsOptions,
  RawBindings = {},
  D = {},
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  E extends EmitsOptions = EmitsOptions,
  EE extends string = string,
  Props = Readonly<ExtractPropTypes<PropsOptions>>,
  Defaults = ExtractDefaultPropTypes<PropsOptions>
> = ComponentOptionsBase<
  Props,
  RawBindings,
  D,
  C,
  M,
  Mixin,
  Extends,
  E,
  EE,
  Defaults
> & {
  props: PropsOptions & ThisType<void>
} & ThisType<
    CreateComponentPublicInstance<
      Props,
      RawBindings,
      D,
      C,
      M,
      Mixin,
      Extends,
      E,
      Props,
      Defaults,
      false
    >
  >
    
export interface ComponentOptionsBase<
  Props,
  RawBindings,
  D,
  C extends ComputedOptions,
  M extends MethodOptions,
  Mixin extends ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin,
  E extends EmitsOptions,
  EE extends string = string,
  Defaults = {}
>
  extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
    ComponentInternalOptions,
    ComponentCustomOptions {
      setup?: (
        this: void,
        props: Props,
        ctx: SetupContext<E, Props>
      ) => Promise<RawBindings> | RawBindings | RenderFunction | void
    //...
  }

Это длинный абзац, давайте воспользуемся упрощенной версией демо, чтобы понять то же самое:

type TypeA<T1, T2, T3> = {
  a: T1,
  b: T2,
  c: T3
}
declare function define<T1, T2, T3>(options: TypeA<T1, T2, T3>): T1
const result = define({
  a: '1',
  b: 1,
  c: {}
}) // result: string

В соответствии с параметром входящих опций ts будут определены типы T1, T2 и T3. Получив T1, T2, T3, вы можете использовать их для других выводов. Немного изменим демонстрацию выше, предполагая, что c — это функция, а тип параметра в ней определяется типом a:

type TypeA<T1, T2, T3> = TypeB<T1, T2>
type TypeB<T1, T2> = {
  a: T1
  b: T2,
  c: (arg:T1)=>{}
}
const result = define({
  a: '1',
  b: 1,
  c: (arg) => {  // arg 这里就被会推导为一个 string 的类型
    return arg
  }
})

Затем посмотрите код в vue, сначалаdefineComponentPropsOptions могут быть получены. Но если props — это объектный тип, то он записывается следующим образом

props: {
   name: {
     type: String,
     //... 其他的属性
   }
}

Для параметра props в настройке нужно извлечь из него тип. Итак, в ComponentOptionsWithObjectProps

export type ComponentOptionsWithObjectProps<
  PropsOptions = ComponentObjectPropsOptions,
  //...
  Props = Readonly<ExtractPropTypes<PropsOptions>>,
  //...
>

Тип пропсоотписей по преобразованию ExtreacPropTypes, а затем, чтобы дать реквизиты, затем пройти компонентОтриологии, внутри, в качестве параметра настройки

export interface ComponentOptionsBase<
  Props,
  //...
>
  extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
    ComponentInternalOptions,
    ComponentCustomOptions {
  setup?: (
    this: void,
    props: Props,
    ctx: SetupContext<E, Props>
  ) => Promise<RawBindings> | RawBindings | RenderFunction | void

Это реализация вывода реквизита.

  • что это делает

    Первым в определении установки является: void . Когда мы напишем логику в функции установки, мы обнаружим, что если использоватьthis.xxxВ IDE будут ошибки

    Property 'xxx' does not exist on type 'void'

    Здесь, установивthis:voidчтобы мы не использовали это в настройке.

    это особое существование в js, оно определяется в соответствии с текущим контекстом, поэтому иногда машинописный текст не может точно определить, какой тип this используется в нашем коде, поэтому это становится любым, каждым Все виды подсказок/выводов типов не могут быть used (примечание: ts выведет тип this только в том случае, если включена конфигурация noImplicitThis). Чтобы решить эту проблему, функция в машинописном тексте может явно написать параметр this, как в примере на официальном сайте:

    interface Card {
      suit: string;
      card: number;
    }
    
    interface Deck {
      suits: string[];
      cards: number[];
      createCardPicker(this: Deck): () => Card;
    }
    
    let deck: Deck = {
      suits: ["hearts", "spades", "clubs", "diamonds"],
      cards: Array(52),
      // NOTE: The function now explicitly specifies that its callee must be of type Deck
      createCardPicker: function (this: Deck) {
        return () => {
          let pickedCard = Math.floor(Math.random() * 52);
          let pickedSuit = Math.floor(pickedCard / 13);
    
          return { suit: this.suits[pickedSuit], card: pickedCard % 13 };
        };
      },
    };
    
    let cardPicker = deck.createCardPicker();
    let pickedCard = cardPicker();
    
    alert("card: " + pickedCard.card + " of " + pickedCard.suit);
    

    В createCardPicker явно указано, что это тип Deck. так вcreateCardPickerСвойства/методы, которые можно использовать при этом, ограничены теми, что есть в колоде.

    Кроме того, в связи с этим есть еще одноThisType.

ExtractPropTypes и ExtractDefaultPropTypes

Как упоминалось выше, реквизит, который мы написали

{
  props: {
    name1: {
      type: String,
      require: true
    },
    name2: {
      type: Number
    }
  }
}

После определенного диривации он преобразуется в тип TS

ReadOnly<{
  name1: string,
  name2?: number | undefined
}>

Этот процесс реализован с помощью ExtractPropTypes.

export type ExtractPropTypes<O> = O extends object
  ? { [K in RequiredKeys<O>]: InferPropType<O[K]> } &
      { [K in OptionalKeys<O>]?: InferPropType<O[K]> }
  : { [K in string]: any }

Хорошо понятен на основе четкого именования в типе: useRrequiredKeys<O>а такжеOptionsKeys<O>Разделите O в зависимости от того, требуется ли это (в качестве примера возьмем предыдущие реквизиты)

{
  name1
} & {
  name2?
}

Затем в каждой группе используйтеInferPropType<O[K]>Выведите тип.

  • InferPropType

    Прежде чем понять это, понять некоторые простые производные. Сначала мы пишем в коде

    props = {
      type: String
    }
    

    Если TS получен, тип пророков - Струйтконструктор. Таким образом, первый шаг требуется для получения соответствующей строки типа / номер et al. Может быть реализован с помощью

    type a = StringConstructor
    type ConstructorToType<T> = T extends  { (): infer V } ? V : never
    type c = ConstructorToType<a> // type c = String
    

    выше мы проходим():infer Vчтобы получить тип. Причина, по которой его можно использовать таким образом, связана с реализацией таких типов, как String/Number. можно написать на javascript

    const key = String('a')
    

    На данный момент ключ имеет строковый тип. Также взгляните на представление типа интерфейса StringConstructor.

    interface StringConstructor {
        new(value?: any): String;
        (value?: any): string;
        readonly prototype: String;
        fromCharCode(...codes: number[]): string;
    }
    

    Eсть():string, так чтоextends {(): infer V}Представленная V - это строка.

    Затем далее измените вышеупомянутое изменение к контенту в пропсоптиках, а затем обратитесь к предположению V в конструкторине для определения

    type a = StringConstructor
    type ConstructorType<T> = { (): T }
    type b = a extends {
      type: ConstructorType<infer V>
      required?: boolean
    } ? V : never // type b = String
    

    Таким образом, содержимое реквизита легко преобразовать в тип в типе.

    Поскольку этот тип реквизита поддерживает многие методы письма на китайском языке, фактическая реализация кода в vue3 более сложна.

    type InferPropType<T> = T extends null
      ? any // null & true would fail to infer
      : T extends { type: null | true }
        ? any 
        // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`
        // 这里单独判断了 ObjectConstructor 和 BooleanConstructor
        : T extends ObjectConstructor | { type: ObjectConstructor }
          ? Record<string, any>
          : T extends BooleanConstructor | { type: BooleanConstructor }
            ? boolean
            : T extends Prop<infer V, infer D> ? (unknown extends V ? D : V) : T
    
    // 支持 PropOptions 和 PropType 两种形式
    type Prop<T, D = T> = PropOptions<T, D> | PropType<T>
    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 type PropType<T> = PropConstructor<T> | PropConstructor<T>[]
    
    type PropConstructor<T = any> =
      | { new (...args: any[]): T & object } // 可以匹配到其他的 Constructor
      | { (): T }  // 可以匹配到 StringConstructor/NumberConstructor 和 () => string 等
      | PropMethod<T> // 匹配到 type: (a: number, b: string) => string 等 Function 的形式
    
    // 对于 Function 的形式,通过 PropMethod 构造成了一个和 stringConstructor 类型的类型
    // PropMethod 作为 PropType 类型之一
    // 我们写 type: Function as PropType<(a: string) => {b: string}> 的时候,就会被转化为
    // type: (new (...args: any[]) => ((a: number, b: string) => {
    //    a: boolean;
    // }) & object) | (() => (a: number, b: string) => {
    //    a: boolean;
    // }) | {
    //     (): (a: number, b: string) => {
    //         a: boolean;
    //     };
    //     new (): any;
    //     readonly prototype: any;
    // }
    // 然后在 InferPropType 中就可以推断出 (a:number,b:string)=> {a: boolean}
    type PropMethod<T, TConstructor = any> = 
      T extends (...args: any) => any // if is function with args
      ? { 
          new (): TConstructor; 
          (): T; 
          readonly prototype: TConstructor 
        } // Create Function like constructor
      : never
    
  • RequiredKeys

    Это используется для отделения ключа, который должен иметь значение от реквизита.Исходный код выглядит следующим образом

    type RequiredKeys<T> = {
      [K in keyof T]: T[K] extends
        | { required: true }
        | { default: any }
        // don't mark Boolean props as undefined
        | BooleanConstructor
        | { type: BooleanConstructor }
        ? K
        : never
    }[keyof T]
    

    Помимо явного определения reqruied, он также содержит значение по умолчанию или логический тип. Потому что для логического значения, если мы его не передаем, оно по умолчанию равно false, а реквизиты со значениями по умолчанию не должны быть неопределенными.

  • OptionalKeys

    С RequiredKeys, OptionsKeys легко: просто исключите RequiredKeys

    type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>
    

ExtractDefaultPropTypes похож на ExtractPropTypes, поэтому я не буду его писать.

Получение возвращаемого значения метода, вычисляемого и данных в параметрах аналогично получению реквизитов выше.

emits options

К параметрам vue3 добавлена ​​новая конфигурация эммитов, которая может отображать конфигурацию событий, которые мы хотим отправить в компоненте. События настроены в эммитах, когда мы пишем$emit, он будет предложен как первый параметр функции.

Способ получения значений конфигурации в emits аналогичен получению типов в props выше.$emit, это черезThisTypeДля достижения (обратитесь к другой статье об ThisType). Ниже приведена упрощенная демонстрация

declare function define<T>(props:{
  emits: T,
  method?: {[key: string]: (...arg: any) => any}
} & ThisType<{
  $emits: (arg: T) => void
}>):T

const result = define({
  emits: {
    key: '123'
  },
  method: {
    fn() {
      this.$emits(/*这里会提示:arg: {
          key: string;
      }*/)
    }
  }
})

Вышеприведенное выводит T как тип в emits. потом& ThisType, чтобы его можно было использовать в методеthis.$emit. Затем используйте T в качестве типа параметра $emit, вы можете написатьthis.$emitкогда будет предложено.

Затем посмотрите на реализацию в vue3

export function defineComponent<
  //... 省却其他的
  E extends EmitsOptions = Record<string, any>,
  //...
>(
  options: ComponentOptionsWithObjectProps<
    //...
    E,
    //...
  >
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>

export type ComponentOptionsWithObjectProps<
  //..
  E extends EmitsOptions = EmitsOptions,
  //...
> = ComponentOptionsBase< // 定义一个 E 的泛型
  //...
  E,
  //...
> & {
  props: PropsOptions & ThisType<void>
} & ThisType<
    CreateComponentPublicInstance<  // 利用 ThisType 实现 $emit 中的提示
      //...
      E,
      //...
    >
  >
    
// ComponentOptionsWithObjectProps 中 包含了 ComponentOptionsBase
export interface ComponentOptionsBase<
  //...
  E extends EmitsOptions, // type EmitsOptions = Record<string, ((...args: any[]) => any) | null> | string[]
  EE extends string = string,
  Defaults = {}
>
  extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
    ComponentInternalOptions,
    ComponentCustomOptions {
      //..
      emits?: (E | EE[]) & ThisType<void>  // 推断出 E 的类型
}
      
export type ComponentPublicInstance<
  //...
  E extends EmitsOptions = {},
  //...
> = {
  //...
  $emit: EmitFn<E>  // EmitFn 来提取出 E 中的 key
  //...
}

Шагая в яму, учась и практикуясь. Переходим к пит-процессу: реализуем процесс деривации эмиссий

export type ObjectEmitsOptions = Record<
  string,
  ((...args: any[]) => any) | null
>
export type EmitsOptions = ObjectEmitsOptions | string[];

declare function define<E extends EmitsOptions = Record<string, any>, EE extends string = string>(options: E| EE[]): (E | EE[]) & ThisType<void>

Затем используйте следующий способ, чтобы проверить результат

const emit = ['key1', 'key2']
const a = define(emit)

Глядя на подсказку ts, я обнаружил, что тип aconst b: string[] & ThisType<void>, но на самом деле, если тот же массив записан в vue3, подсказкаconst a: (("key1" | "key2")[] & ThisType<void>) | (("key1" | "key2")[] & ThisType<void>)

После долгой борьбы я наконец нашел разницу в способе написания: результаты согласуются со следующими словами.

define(['key1', 'key2'])

Но используя предыдущий метод записи, при передаче через переменную, когда ts получает emit, его тип выводится какstring[], поэтому тип, полученный в функции определения, становитсяstring[], вместо оригинала['key1', 'key2']

Поэтому необходимо обратить внимание: при определении эммитов в vue3 рекомендуется писать прямо в эммитах, а не извлекать его как отдельную переменную и потом передавать в эммиты

Если его действительно нужно поместить в отдельную переменную, его нужно обработать так, чтобы'[key1', 'key2']Тип возврата определения переменной:['key1', 'key2']вместоstring[]. Вы можете использовать следующие два способа:

  • метод первый

    const keys = ["key1", "key2"] as const; // const keys: readonly ["key1", "key2"]
    

    Так проще написать. Но есть недостаток, ключи конвертируются в readonly, и ключи нельзя модифицировать потом.

    Справочная статья2 ways to create a Union from an Array in Typescript

  • Способ 2

    type UnionToIntersection<T> = (T extends any ? (v: T) => void : never) extends (v: infer V) => void ? V : never
    type LastOf<T> = UnionToIntersection<T extends any ? () => T : never> extends () => infer R ? R : never
    type Push<T extends any[], V> = [ ...T, V]
    
    type UnionToTuple<T, L = LastOf<T>, N = [T] extends [never] ? true : false> = N extends true ? [] : Push<UnionToTuple<Exclude<T, L>>, L>
    
    declare function tuple<T extends string>(arr: T[]): UnionToTuple<T>
    
    const c = tuple(['key1', 'key2']) // const c: ["key1", "key2"]
    

    сначала черезarr: T[]Буду['key1', 'key2']Преобразовать в объединение, а затем рекурсивно,LastOfполучить последний в союзе,Pushв массив.

миксины и расширения

Контент, написанный в миксинах или расширениях в vue3, можно найти вthisподскажите в. Для примесей и расширений есть одно большое отличие от других типов вывода выше: рекурсия. Следовательно, рекурсивная обработка также требуется при вынесении суждений о типах. Простой пример выглядит следующим образом

const AComp = {
  methods: {
    someA(){}
  }
}
const BComp = {
  mixins: [AComp],
  methods: {
    someB() {}
  }
}
const CComp = {
  mixins: [BComp],
  methods: {
    someC() {}
  }
}

Для намека на это в CComp должны быть методы someB и someA. Чтобы реализовать эту подсказку, при выполнении вывода типа требуется ThisType, подобный следующему.

ThisType<{
  someA
} & {
  someB
} & {
  someC
}>

Итак, для обработки миксинов необходимо рекурсивно получить содержимое миксинов в компоненте, а затем преобразовать вложенные типы в плоские, которые связаны &. См. реализацию в исходном коде:

// 判断 T 中是否有 mixin
// 如果 T 含有 mixin 那么这里结果为 false,以为 {mixin: any} {mixin?: any} 是无法互相 extends 的
type IsDefaultMixinComponent<T> = T extends ComponentOptionsMixin
  ? ComponentOptionsMixin extends T ? true : false
  : false

// 
type IntersectionMixin<T> = IsDefaultMixinComponent<T> extends true
  ? OptionTypesType<{}, {}, {}, {}, {}>  // T 不包含 mixin,那么递归结束,返回 {}
  : UnionToIntersection<ExtractMixin<T>> // 获取 T 中 Mixin 的内容进行递归

// ExtractMixin(map type) is used to resolve circularly references
type ExtractMixin<T> = {
  Mixin: MixinToOptionTypes<T>
}[T extends ComponentOptionsMixin ? 'Mixin' : never]

// 通过 infer 获取到 T 中 Mixin, 然后递归调用 IntersectionMixin<Mixin>
type MixinToOptionTypes<T> = T extends ComponentOptionsBase<
  infer P,
  infer B,
  infer D,
  infer C,
  infer M,
  infer Mixin,
  infer Extends,
  any,
  any,
  infer Defaults
>
  ? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, Defaults & {}> &
      IntersectionMixin<Mixin> &
      IntersectionMixin<Extends>
  : never

Процесс одинаков для расширений и миксинов. Затем посмотрите на обработку в ThisType

ThisType<
    CreateComponentPublicInstance<
      Props,
      RawBindings,
      D,
      C,
      M,
      Mixin,
      Extends,
      E,
      Props,
      Defaults,
      false
    >
  >
export type CreateComponentPublicInstance<
  P = {},
  B = {},
  D = {},
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  E extends EmitsOptions = {},
  PublicProps = P,
  Defaults = {},
  MakeDefaultsOptional extends boolean = false,
  // 将嵌套的结构转为扁平化的
  PublicMixin = IntersectionMixin<Mixin> & IntersectionMixin<Extends>,
  // 提取 props
  PublicP = UnwrapMixinsType<PublicMixin, 'P'> & EnsureNonVoid<P>,
  // 提取 RawBindings,也就是 setup 返回的内容
  PublicB = UnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
  // 提取 data 返回的内容
  PublicD = UnwrapMixinsType<PublicMixin, 'D'> & EnsureNonVoid<D>,
  PublicC extends ComputedOptions = UnwrapMixinsType<PublicMixin, 'C'> &
    EnsureNonVoid<C>,
  PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
    EnsureNonVoid<M>,
  PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
    EnsureNonVoid<Defaults>
> = ComponentPublicInstance< // 上面结果传给 ComponentPublicInstance,生成 this context 中的内容
  PublicP,
  PublicB,
  PublicD,
  PublicC,
  PublicM,
  E,
  PublicProps,
  PublicDefaults,
  MakeDefaultsOptional,
  ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, Defaults>
>

Выше приведена реализация большей части defineComponent в целом.Видно, что он был рожден исключительно для вывода типов.В то же время здесь используется много типов методов вывода, а некоторые здесь не рассматриваются.Заинтересованные студенты можно перейти к более подробному рассмотрению реализации в Vue.