-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
288 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}, | ||
] | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |