◆
Vue vue-types 基础用法与 Props 类型校验
Vue 原生的 props 声明写起来很啰嗦:一个带默认值的必填字符串要写成 { type: String, required: true, default: 'x' },想限定只能取几个特定值还得自己写 validator。而且原生 type: String 在 TS 里只能推出 string,没法表达「只能是 'apple' | 'pear'」这种联合类型。
vue-types 用链式调用把这些收敛成一行:string().def('x')、number<1|2|3>().def(1),同时补上了 TS 泛型标注能力——泛型管编译期类型检查,def/isRequired/validate 管运行时校验,两套机制各管一段。
这篇是基础类型的用法验证。代码里保留了大量被注释掉的错误示例和对应的 TS 报错信息,这些反例比正例更有价值,能直接看出哪些写法编译期就会被拦下、哪些要到运行时才报错、以及少数「TS 报错但实际能跑」的边界情况。
基础使用示例如下
这段把 number、string、bool、func、object、array、any、symbol、integer 逐个走了一遍。几处值得留意的地方:
- 两种调用风格等价:
VueTypes.number.def(10)是属性式,number().def(15)是函数式;带泛型时必须用函数式。 - 默认值差异:
integer自带默认值 0,而number()没有默认值。 - 泛型收窄是编译期能力:
number<1|2|3>().def(4)会被 TS 直接拦住,运行时并不会校验这个范围。 - 枚举那几条是个坑:
string<Fruits>().def(Fruits.Apple)运行时会报错,反而写字面量'Apple'能跑,代价是 TS 报类型不匹配。 object().def([])传数组会被运行时校验拦下(控制台输出[VueTypes warn]: object - invalid default value),因为数组不是合法的对象默认值。func().def(console.log(1))传的是调用结果undefined而不是函数,TS 会报错;正确写法是.def(() => console.log(1))。- 最后的
validate是自定义运行时校验,回调返回布尔值决定是否通过。
底部把 props 解构出来塞进 reactive 数组渲染,是为了在页面上直接看到每个 prop 最终拿到的值——尤其是没传值时默认值到底是什么。注意解构 props 会丢失响应性,这里只为展示初始值,实际业务代码里不该这么写。
<template>
<div v-for="(item, index) in testArrs" :key="index">
<div>{{ item.name }}:{{ item.value }}</div>
</div>
</template>
<script lang="ts" setup>
import VueTypes, {
number,
string,
integer,
any,
array,
bool,
func,
object,
symbol,
} from 'vue-types';
import { reactive } from 'vue';
interface Users {
id: number;
username: string;
}
enum Fruits {
Apple = 'Apple',
Pear = 'Pear',
}
const props = defineProps({
/** 直接使用引入的 VueTypes 测试 */
numberPriDef: VueTypes.number.def(10),
stringEmpty: VueTypes.string,
/** 声明了 isRequired 代表必传 否则控制台会抛出一个错误提示:Missing required prop: "stringReqDef" */
// stringReqDef: VueTypes.string.isRequired.def('123'),
/** integer 的默认值是 0 */
integerEmpty: VueTypes.integer,
numberDef: number().def(15),
/** number 类型没有默认值 */
numberEmpty: number(),
/** 必须是 1、2、3 中的某一个 */
numberLimit: number<1 | 2 | 3>().def(1),
/** TS2345: Argument of type '4' is not assignable to parameter of type '1 | 2 | 3 | undefined'. */
// numberLimitOne: number<1|2|3>().def(4),
stringDef: string().def('testString'),
/** 枚举 */
stringEnum: string<Fruits>(),
/** 下面这种写法在运行时会直接报错 */
// stringEnumOne: string<Fruits>().def(Fruits.Apple),
/**
* TS2345: Argument of type '"Apple"' is not assignable to parameter of type 'Fruits | undefined'.
* 但实际上运行不会有问题
*/
stringEnumTwo: string<Fruits>().def('Apple'),
/** 联合类型 */
stringUnion: string<'apple' | 'pear'>(),
stringUnionOne: string<'apple' | 'pear'>().def(undefined),
stringUnionTwo: string<'apple' | 'pear'>().def('apple'),
/** TS2345: Argument of type '"apples"' is not assignable to parameter of type '"apple" | "pear" | undefined'. */
// stringUnionThree: string<'apple'|'pear'>().def('apples'),
boolEmpty: bool(),
uniq: symbol(),
funcEmpty: func(),
/** TS2345: Argument of type 'void' is not assignable to parameter of type '((...args: any[]) => any) | undefined'. */
// funcEmptyOne: func().def(console.log(1)),
funcEmptyTwo: func().def(() => console.log(1)),
funcEmptyThree: func().def(undefined),
funcParams: func<(event: Event) => void>().def(() => 1),
funcParamsOne: func<(event: string) => void>().def(() => {
return 1;
}),
anyDef: any().def(null),
anyKnown: any<unknown>(),
obj: object().def({}),
/** 控制台抛出错误:[VueTypes warn]: object - invalid default value: "" */
// objOne: object().def([]),
/** TS2345: Argument of type '1' is not assignable to parameter of type 'DefaultFactory{ [key: string]: any; }> | */
// userObjTwo: object().def(1),
objThree: object().def({ a: 1 }),
objFour: object<Users>().def({ id: 1, username: 'wfd' }),
/** Argument of type '{ id: number; username: string; age: number; }' is not assignable to parameter of type 'DefaultFactory<Users> | undefined'. */
// objFive: object<Users>().def({id: 1, username: 'wfd', age: 33}),
arrEmpty: array<string>(),
arrEmptyOne: array<string>().def(['11', '22']),
/** TS2322: Type 'number' is not assignable to type 'string'. */
// arrEmptyTwo: array<string>().def(['11', 22]),
/** 指定允许的数组项 */
arrOne: array<'apple' | 'pear'>().def(['pear', 'apple']),
/** TS2322: Type '"egg"' is not assignable to type '"apple" | "pear"'. */
// arrTwo: array<'apple'|'pear'>().def(['pear', 'apple', 'egg']),
/** 自定义验证器 */
testValidate: string()
.def('11')
.validate((value: unknown | string) => {
return (value as string).includes('1');
}),
});
const {
numberPriDef,
stringEmpty,
integerEmpty,
numberDef,
numberEmpty,
numberLimit,
stringDef,
stringEnum,
stringUnion,
boolEmpty,
uniq,
funcEmpty,
funcParams,
anyDef,
anyKnown,
} = props;
const testArrs = reactive([
{ name: 'numberPriDef', value: numberPriDef },
{ name: 'stringEmpty', value: stringEmpty },
{ name: 'integerEmpty', value: integerEmpty },
{ name: 'numberDef', value: numberDef },
{ name: 'numberEmpty', value: numberEmpty },
{ name: 'numberLimit', value: numberLimit },
{ name: 'stringDef', value: stringDef },
{ name: 'stringEnum', value: stringEnum },
{ name: 'stringUnion', value: stringUnion },
{ name: 'boolEmpty', value: boolEmpty },
{ name: 'uniq', value: uniq },
{ name: 'funcEmpty', value: funcEmpty },
{ name: 'funcParams', value: funcParams },
{ name: 'anyDef', value: anyDef },
{ name: 'anyKnown', value: anyKnown },
]);
</script>
<style scoped></style>

还没有留言,来发表第一条吧~