Skip to content

Commit

Permalink
feat(utils): 新增 makeEnum
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Nov 17, 2021
1 parent a249469 commit 5a6f1a7
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 0 deletions.
74 changes: 74 additions & 0 deletions src/utils/__snapshots__/makeEnum.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`makeEnum $buildList 正确 1`] = `
Array [
Object {
"label": "x",
"value": 1,
},
Object {
"label": "y",
"value": 2,
},
]
`;

exports[`makeEnum $buildList 正确 2`] = `
Array [
Object {
"label": "",
"value": true,
},
Object {
"label": "",
"value": false,
},
]
`;

exports[`makeEnum $list 正确 1`] = `
Array [
Object {
"label": "x",
"value": 1,
},
Object {
"label": "y",
"value": 2,
},
Object {
"label": "z",
"value": 3,
},
]
`;

exports[`makeEnum $list 正确 2`] = `
Array [
Object {
"label": "x",
"value": "1",
},
Object {
"label": "y",
"value": "2",
},
Object {
"label": "z",
"value": "3",
},
]
`;

exports[`makeEnum $list 正确 3`] = `
Array [
Object {
"label": "x",
"value": true,
},
Object {
"label": "y",
"value": false,
},
]
`;
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export * from './keysStrict'
export * from './loadCss'
export * from './loadResource'
export * from './loopUntil'
export * from './makeEnum'
export * from './md5'
export * from './move'
export * from './omitStrict'
Expand Down
121 changes: 121 additions & 0 deletions src/utils/makeEnum.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { makeEnum } from './makeEnum'

describe('makeEnum', () => {
test('支持数字', () => {
expect(
makeEnum({
x: 1,
y: 2,
z: 3,
}),
).toMatchObject({
x: 1,
y: 2,
z: 3,
1: 'x',
2: 'y',
3: 'z',
})
})

test('支持字符串', () => {
expect(
makeEnum({
x: '1',
y: '2',
z: '3',
}),
).toMatchObject({
x: '1',
y: '2',
z: '3',
1: 'x',
2: 'y',
3: 'z',
})
})

test('支持布尔值', () => {
expect(
makeEnum({
x: true,
y: false,
}),
).toMatchObject({
x: true,
y: false,
true: 'x',
false: 'y',
})
})

test('$list 正确', () => {
expect(
makeEnum({
x: 1,
y: 2,
z: 3,
}).$list,
).toMatchSnapshot()
expect(
makeEnum({
x: '1',
y: '2',
z: '3',
}).$list,
).toMatchSnapshot()
expect(
makeEnum({
x: true,
y: false,
}).$list,
).toMatchSnapshot()
})

test('$buildList 正确', () => {
expect(
makeEnum({
x: 1,
y: 2,
z: 3,
}).$buildList(['x', 'y']),
).toMatchSnapshot()
expect(
makeEnum({
x: true,
y: false,
}).$buildList({
x: '是',
y: '否',
}),
).toMatchSnapshot()
})

test('$is 正确', () => {
const status = makeEnum({
x: '1',
y: '2',
z: '3',
})
expect(status.$is('1', 'x')).toBeTrue()
expect(status.$is('1', '1')).toBeTrue()
expect(status.$is('1', 'y')).toBeFalse()
expect(status.$is('x', 'x')).toBeTrue()
expect(status.$is('x', '1')).toBeTrue()
expect(status.$is('x', 'y')).toBeFalse()

const status2 = makeEnum({
x: 1,
y: 2,
z: 3,
})
expect(status2.$is(1, 'x')).toBeTrue()
expect(status2.$is(1, 1)).toBeTrue()

const yesOrNo = makeEnum({
x: true,
y: false,
})
expect(yesOrNo.$is(false, 'x')).toBeFalse()
})
})
92 changes: 92 additions & 0 deletions src/utils/makeEnum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { castArray, invert } from 'lodash-uni'
import { LiteralUnion, OneOrMore, ValueOf } from '../types'

export type EnumKey = string
export type EnumValue = string | number | boolean
export type EnumMap = Record<EnumKey, EnumValue>

// https://stackoverflow.com/questions/56415826/is-it-possible-to-precisely-type-invert-in-typescript
type AllValues<T extends Record<EnumKey, EnumValue>> = {
[P in keyof T]: {
label: P
value: T[P] extends true ? 'true' : T[P] extends false ? 'false' : T[P]
}
}[keyof T]
type InvertResult<T extends Record<EnumKey, EnumValue>> = {
// @ts-ignore
[P in AllValues<T>['value']]: Extract<AllValues<T>, { value: P }>['label']
}

type List<T extends Record<EnumKey, EnumValue>> = Array<
{
[P in keyof T]: {
label: P
value: T[P]
}
}[keyof T]
>

export type EnumResult<T extends EnumMap> = T &
InvertResult<T> & {
$list: List<T>
$buildList: (keys: Array<keyof T> | Record<keyof T, any>) => List<T>
$is(value: any, keys: OneOrMore<LiteralUnion<keyof T, ValueOf<T>>>): boolean
}

/**
* 构造枚举数据。
*
* @param map 枚举映射数据
*/
export function makeEnum<T extends EnumMap>(map: T): EnumResult<T> {
const res: EnumResult<T> = {
...map,
...invert(map),
} as any
Object.defineProperties(res, {
$list: {
value: Object.keys(map).reduce<EnumResult<T>['$list']>((res, key) => {
res.push({
label: key,
value: map[key] as any,
})
return res
}, [] as any),
enumerable: false,
writable: false,
configurable: false,
},
$buildList: {
value: (keys => {
const labelMap = Array.isArray(keys) ? undefined : keys
keys = Array.isArray(keys) ? keys : Object.keys(keys)
return Object.keys(map).reduce<ReturnType<EnumResult<T>['$buildList']>>(
(res, key) => {
if (keys.includes(key)) {
res.push({
label: labelMap?.[key] || key,
value: map[key] as any,
})
}
return res
},
[] as any,
) as any
}) as EnumResult<T>['$buildList'],
enumerable: false,
writable: false,
configurable: false,
},
$is: {
value: ((value, keys) => {
return castArray(keys).some(
key => value === key || value === res[key as any],
)
}) as EnumResult<T>['$is'],
enumerable: false,
writable: false,
configurable: false,
},
})
return res
}

0 comments on commit 5a6f1a7

Please sign in to comment.