default-template
Version:
一个用于定义默认值和转换对象结构的实用工具
286 lines (258 loc) • 12.2 kB
text/typescript
/// <reference types="vitest" />
import { describe, it, expect } from 'vitest';
import defaultTemplate, { TemplateRename } from './index';
describe('defaultTemplate', () => {
it('基本类型:undefined/null 返回默认值', () => {
const result1 = defaultTemplate(10, undefined);
console.log('基本类型:undefined/null 返回默认值', result1);
expect(result1).toBe(10);
const result2 = defaultTemplate(10, null);
console.log('基本类型:undefined/null 返回默认值', result2);
expect(result2).toBe(10);
const result3 = defaultTemplate(false, undefined);
console.log('基本类型:undefined/null 返回默认值', result3);
expect(result3).toBe(false);
const result4 = defaultTemplate('', undefined);
console.log('基本类型:undefined/null 返回默认值', result4);
expect(result4).toBe('');
});
it('基本类型:类型自动转换', () => {
const result1 = defaultTemplate(10, '20');
console.log('基本类型:类型自动转换', result1);
expect(result1).toBe(20);
const result2 = defaultTemplate(false, 'true');
console.log('基本类型:类型自动转换', result2);
expect(result2).toBe(true);
const result3 = defaultTemplate(false, 'false');
console.log('基本类型:类型自动转换', result3);
expect(result3).toBe(false);
const result4 = defaultTemplate('', 123);
console.log('基本类型:类型自动转换', result4);
expect(result4).toBe('123');
const result5 = defaultTemplate(BigInt(1), '2');
console.log('基本类型:类型自动转换', result5);
expect(result5).toBe('2'); // bigint 作为模板时,字符串原样返回
const result6 = defaultTemplate(Symbol('a'), Symbol('b'));
console.log('基本类型:类型自动转换', typeof result6);
expect(typeof result6).toBe('symbol');
});
it('基本类型:function 作为模板', () => {
const fn = (x: any) => (typeof x === 'number' ? x * 2 : 0);
let result = defaultTemplate(fn, 5);
console.log('基本类型:function 作为模板', result);
expect(result).toBe(10);
result = defaultTemplate(fn, undefined);
console.log('基本类型:function 作为模板', result);
expect(result).toBe(0);
});
it('基本类型:Date 作为模板', () => {
const d = new Date('2020-01-01');
const d2 = new Date('2022-01-01');
let result = defaultTemplate(d, d2);
console.log('基本类型:Date 作为模板', result);
expect(result).toBe(d2);
result = defaultTemplate(d, undefined);
console.log('基本类型:Date 作为模板', result);
expect(result).toBe(d);
});
it('基本类型:原样返回', () => {
let result = defaultTemplate(null, 10);
console.log('基本类型:原样返回', result);
expect(result).toBe(10);
result = defaultTemplate(null, '10');
console.log('基本类型:原样返回', result);
expect(result).toBe('10');
result = defaultTemplate(null, { a: 1 });
console.log('基本类型:原样返回', result);
expect(result).toEqual({ a: 1 });
});
it('数组:元素模板', () => {
const result1 = defaultTemplate([10], [null, '20', 20]);
console.log('数组:元素模板', result1);
expect(result1).toEqual([10, 20, 20]);
const result2 = defaultTemplate([false], [1, 0, 'false']);
console.log('数组:元素模板', result2);
expect(result2).toEqual([true, false, false]);
const result3 = defaultTemplate([{ a: 1 }], [{ a: 2, b: 3 }, { a: '3', b: 4 }]);
console.log('数组:元素模板', result3);
// 只保留模板字段 a
expect(result3).toEqual([{ a: 2 }, { a: 3 }]);
});
it('数组:嵌套数组', () => {
const result = defaultTemplate([[0]], [[1, 2], [3, null]]);
console.log('数组:嵌套数组', result);
expect(result).toEqual([[1, 2], [3, 0]]);
});
it('数组:空模板数组', () => {
let result = defaultTemplate([], [1, 2, 3]);
console.log('数组:空模板数组', result);
expect(result).toEqual([1, 2, 3]);
result = defaultTemplate([], undefined);
console.log('数组:空模板数组', result);
expect(result).toEqual([]);
});
it('对象:基础对象模板', () => {
const result = defaultTemplate({ a: 10, b: false, c: 20 }, { a: '20', b: null, d: 'test' });
console.log('对象:基础对象模板', result);
expect(result).toEqual({ a: 20, b: false, c: 20 });
});
it('对象:深层嵌套对象', () => {
const def = { a: { b: { c: 1 } } };
const src = { a: { b: { c: '2', d: 3 }, e: 4 } };
const result = defaultTemplate(def, src);
console.log('对象:深层嵌套对象', result);
expect(result).toEqual({ a: { b: { c: 2 } } });
});
it('对象:通配符模板', () => {
const result1 = defaultTemplate({ '?': true }, { x: null, y: false });
console.log('对象:通配符模板', result1);
expect(result1).toEqual({ x: true, y: false });
const result2 = defaultTemplate({ a: 1, '?': 0 }, { a: 2, b: '3', c: null }) as any;
console.log('对象:通配符模板', result2);
expect(result2).toEqual({ a: 2, b: 3, c: 0 });
});
it('对象:通配符与显式key混用', () => {
const result = defaultTemplate({ a: 1, '?': 2 }, { a: undefined, b: 3 });
console.log('对象:通配符与显式key混用', result);
expect(result).toEqual({ a: 1, b: 3 });
});
it('对象:自定义通配符键名', () => {
const result = defaultTemplate({ '*': 1 }, { a: null, b: 2 }, { allTemplateKey: '*' });
console.log('对象:自定义通配符键名', result);
expect(result).toEqual({ a: 1, b: 2 });
});
it('对象:空对象模板', () => {
let result = defaultTemplate({}, { a: 1 });
console.log('对象:空对象模板', result);
expect(result).toEqual({});
result = defaultTemplate({}, {});
console.log('对象:空对象模板', result);
expect(result).toEqual({});
});
it('对象:嵌套结构', () => {
const def = { user: { name: '', age: 0, tags: [''] }, active: false };
const src = { user: { name: '张三', age: '18', tags: [null, 'vip'] }, active: 'true', extra: 123 };
const result = defaultTemplate(def, src);
console.log('对象:嵌套结构', result);
expect(result).toEqual({ user: { name: '张三', age: 18, tags: ['', 'vip'] }, active: true });
});
it('对象:键名映射', () => {
const def = { id: 0, name: new TemplateRename('username', '') };
const src = { id: 1, name: '张三' };
const result = defaultTemplate(def, src);
console.log('对象:键名映射', result);
expect(result).toEqual({ id: 1, username: '张三' });
});
it('对象:键名映射为函数', () => {
const def = { foo: new TemplateRename(k => 'bar_' + k, 1) };
const src = { foo: 2 };
const result = defaultTemplate(def, src);
console.log('对象:键名映射为函数', result);
expect(result).toEqual({ bar_foo: 2 });
});
it('对象:TemplateRename 嵌套', () => {
const def = { a: new TemplateRename('b', new TemplateRename('c', 1)) };
const src = { a: 2 };
const result = defaultTemplate(def, src);
console.log('对象:TemplateRename 嵌套', result);
expect(result).toEqual({ c: 2 });
});
it('自定义处理函数', () => {
const result1 = defaultTemplate((data: string | any[]) => Array.isArray(data) ? data.length : 0, [1, 2, 3]);
console.log('自定义处理函数', result1);
expect(result1).toBe(3);
const result2 = defaultTemplate((data: string) => typeof data === 'string' ? data.toUpperCase() : '', 'abc') as any;
console.log('自定义处理函数', result2);
expect(result2).toBe('ABC');
// 测试 owner 参数
const result3 = defaultTemplate((data: any, owner: any) => owner && owner.flag ? 1 : 0, 5, undefined) as any;
console.log('自定义处理函数', result3);
expect(result3).toBe(0);
const result4 = defaultTemplate((data: any, owner: any) => owner && owner.flag ? 1 : 0, 5, undefined) as any;
console.log('自定义处理函数', result4);
expect(result4).toBe(0);
const result5 = defaultTemplate((data: any, owner: any) => owner && owner.flag ? 1 : 0, 5, undefined) as any;
console.log('自定义处理函数', result5);
expect(result5).toBe(0);
});
it('边界:模板为 null/undefined', () => {
const result1 = defaultTemplate(null, 123);
console.log('边界:模板为 null/undefined', result1);
expect(result1).toBe(123);
const result2 = defaultTemplate(undefined, 456) as any;
console.log('边界:模板为 null/undefined', result2);
expect(result2).toBe(456);
});
it('边界:src 为 null/undefined', () => {
const result1 = defaultTemplate(1, null);
console.log('边界:src 为 null/undefined', result1);
expect(result1).toBe(1);
const result2 = defaultTemplate({ a: 1 }, null);
console.log('边界:src 为 null/undefined', result2);
expect(result2).toEqual({ a: 1 });
const result3 = defaultTemplate([1], null) as any;
console.log('边界:src 为 null/undefined', result3);
expect(result3).toEqual([]);
});
it('边界:对象模板未声明的字段被删除', () => {
const result = defaultTemplate({ a: 1 }, { a: 2, b: 3 });
console.log('边界:对象模板未声明的字段被删除', result);
expect(result).toEqual({ a: 2 });
});
it('边界:数组模板长度为0', () => {
let result = defaultTemplate([], [1, 2, 3]);
console.log('边界:数组模板长度为0', result);
expect(result).toEqual([1, 2, 3]);
result = defaultTemplate([], undefined);
console.log('边界:数组模板长度为0', result);
expect(result).toEqual([]);
});
it('极端情况:模板和数据都为 undefined/null/空', () => {
const result1 = defaultTemplate(undefined, undefined) as any;
console.log('极端情况:模板和数据都为 undefined/null/空', result1);
expect(result1).toBe(undefined);
const result2 = defaultTemplate(null, null) as any;
console.log('极端情况:模板和数据都为 undefined/null/空', result2);
expect(result2).toBe(null);
const result3 = defaultTemplate({}, undefined) as any;
console.log('极端情况:模板和数据都为 undefined/null/空', result3);
expect(result3).toEqual({});
const result4 = defaultTemplate([], undefined) as any;
console.log('极端情况:模板和数据都为 undefined/null/空', result4);
expect(result4).toEqual([]);
});
it('异常输入:模板为非对象/数组/基本类型/函数', () => {
const result1 = defaultTemplate(new Set([1]), [1]);
console.log('异常输入:模板为非对象/数组/基本类型/函数', result1);
expect(result1).toEqual(new Set([1]));
const result2 = defaultTemplate(new Map([[1, 2]]), { a: 1 }) as any;
console.log('异常输入:模板为非对象/数组/基本类型/函数', result2);
expect(result2).toEqual(new Map([[1, 2]]));
});
it('泛型推断:类型安全', () => {
type User = { id: number; name: string; active: boolean };
const def: User = { id: 0, name: '', active: false };
const user = defaultTemplate(def, { id: '1', name: '张三', active: 'true' });
console.log('泛型推断:类型安全', user);
expect(user).toEqual({ id: 1, name: '张三', active: true });
});
it('数组模板:字段补全', () => {
const result = defaultTemplate([{ min: 0 }], [{ val: -1 }, { val: 5 }]);
console.log('数组模板:字段补全', result);
// 只保留模板字段 min
expect(result).toEqual([{ min: 0 }, { min: 0 }]);
});
it('嵌套对象+数组:只保留模板字段', () => {
const result = defaultTemplate(
{ totalCount: 0, list: [{ id: 0, title: '', xxx: 'xxx' }] },
{ list: [{ aaa: 'xx' }] }
);
console.log('嵌套对象+数组:只保留模板字段', result);
expect(result).toEqual({
totalCount: 0,
list: [
{ id: 0, title: '', xxx: 'xxx' }
]
});
});
});