关于vue3:vue3-父组件给子组件传递泛型不用JSX

最近在封装一个组件,应用的时候心愿父组件能给子组件传递一个泛型,在网上搜了半天,答案都是说要用jsx能力实现。具体写法如下:

应用JSX

这段代码来自Eluxjs的示例我的项目elux-vue-antd-admin,感兴趣的能够看下。
父组件: (为缩小篇幅,代码删了很多,当伪代码看吧)

const Component = defineComponent<Props>({
  props: ['listPathname', 'mergeColumns'] as any,
  setup(props) {
    return () => {
      return (
        <MTable<ListItem> // 这里应用子组件MTable,传递泛型ListItem
          size={tableSize}
          commonActions={commonActions}
          selection={selection}
          loading={listLoading === 'Start' || listLoading === 'Depth'}
        />
      );
    };
  },
});

export default Component;

子组件:(子组件比拟麻烦)

interface Props<T = Record<string, any>> extends TableProps<T> {
  class?: string;
  columns: MColumns<T>[];
  selectedRows?: Partial<T>[];
  selection?: MSelection<T>;
}

const Component = defineComponent<Props>({
  name: styles.root,
  props: [
    'class',
    'columns'
  ] as any,
  setup(props: Props) {
    return () => {
      const {class: className = '', dataSource, onChange, rowKey = 'id', loading, size, bordered, locale, scroll} = props;
      return (
        <div class={styles.root + ' ' + className}>
          {headArea.value}
          <Table
            columns={columnList.value}
            rowSelection={rowSelection.value}
            onChange={onChange}
            size={size}
          />
        </div>
      );
    };
  },
}) as any;

export default Component as <T>(props: Props<T>) => JSX.Element;

这里应用 Component as <T>(props: Props<T>) => JSX.Element; 来做到接管泛型,但生产它的父组件必须用 jsx。
因为我的项目不不便用jsx,我找了很久如何用template来传递泛型,终于找到了!

应用Template

先看子组件:

<script
  lang="ts"
  setup
  // 要害:setup script有个generic属性,能够申明泛型,供组件应用
  generic="T extends { name: string; age: number; }, Q extends { title:string }"
>
// 在props中应用泛型,rowKey应用keyof T,plainObj应用第二个泛型Q
defineProps<{
  personList: T[];
  rowKey: keyof T;
  plainObj: Q;
}>();
</script>

<template>
  <div>
    // 模板中失常应用props
    <h3 v-for="item in personList" :key="item.name">
      {{ item.age }}
    </h3>
    // 用插槽向父组件裸露数据,plainObj的类型是Q
    <slot :obj="plainObj" />
  </div>
</template>

generic属性的作用就是给组件中应用的泛型提供一个“起源”,这样父组件生产子组件的时候,只有给personList传递一个数据,泛型T就会被主动推断进去。

generic属性的泛型能够抉择extends一些已知的属性,因为子组件本身应用到props的哪些属性,和生产该组件的中央是无关的。

父组件:

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue';
// 给list的item申明类型,该类型能够是子组件T的超集
interface DataType {
  name: string;
  age: number;
  gender: 'male' | 'female' | 'wallmart bag';
  hobby?: 'jk' | 'dk' | '敌法师';
}
const list: DataType[] = [
  {
    name: 'ming',
    age: 20,
    gender: 'male',
    hobby: '敌法师'
  },
  {
    name: 'hong',
    age: 20,
    gender: 'female'
  },
  {
    name: 'hua',
    age: 20,
    gender: 'wallmart bag'
  }
];
</script>

<template>
  <div>
    <HelloWorld
      :person-list="list"
      row-key="genderr"
      :plainObj="{ happy: true, title: '低头' }"
      v-slot="{ obj }"
    >
      <!-- 👆 不能将类型“"agg"”调配给类型“keyof T”。 -->

      {{ obj.happ }}
      <!-- 👆 属性“happ”在类型“{ happy: boolean; title: string; }”上不存在。你是否指的是“happy”? -->
    </HelloWorld>
  </div>
</template>

<style></style>

能够看到,子组件推断出了T的类型是DataType,报错 类型"genderr"不可调配给类型“keyof T”。你的意思是"gender"?
Q也胜利的推断了进去,报错 属性“happ”在类型“{ happy: boolean; title: string; }”上不存在,这里的 { happy: boolean; title: string; } 即是咱们传给 plainObj 属性的泛型Q

这样就实现了父组件通过props,给子组件传递所需的泛型

generic属性的泛型如何extends一些内部的类型,或者extends已有的接口呢?
额定应用一个script标签即可

<script>
// 额定应用一个script标签即可
import { SecendType } from "./index";
interface FirstType {
  name: string;
  age: number;
}
</script>

<script lang="ts" setup generic="T extends FirstType, Q extends SecendType">
defineProps<{
  personList: T[];
  rowKey: keyof T;
  plainObj: Q;
}>();
</script>

<template>
  <div>
    <h3 v-for="item in personList" :key="item.name">
      {{ item.age }}
    </h3>
    <slot :obj="plainObj" />
  </div>
</template>

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理