Skip to content

Commit bce2bf4

Browse files
refactor(types): name the trait value shape (#8366)
1 parent cb66498 commit bce2bf4

4 files changed

Lines changed: 65 additions & 10 deletions

File tree

frontend/common/types/responses.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,16 @@ export type FeatureStateValue = {
595595
type: 'int' | 'unicode' | 'bool' | 'float'
596596
}
597597

598+
// The trait shape from the core API, which keys its type as `value_type`
599+
// where feature states use `type`.
600+
export type TraitValue = {
601+
boolean_value: boolean | null
602+
float_value?: number | null
603+
integer_value: number | null
604+
string_value: string | null
605+
value_type: 'int' | 'unicode' | 'bool' | 'float'
606+
}
607+
598608
export type MultivariateOption = {
599609
id: number
600610
uuid: string

frontend/common/utils/__tests__/featureStateToValue.test.ts

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { featureStateToValue } from 'common/utils/featureStateToValue'
2+
import type { FlagsmithValue, TraitValue } from 'common/types/responses'
23

34
describe('featureStateToValue', () => {
45
it.each([
@@ -10,10 +11,51 @@ describe('featureStateToValue', () => {
1011
expect(featureStateToValue(nested as never)).toBe(expected)
1112
})
1213

13-
it('reads value_type when present (core traits)', () => {
14-
expect(
15-
featureStateToValue({ integer_value: 3, value_type: 'int' } as never),
16-
).toBe(3)
14+
// Typed rather than cast, so these fail to compile if TraitValue drifts.
15+
it.each<[string, TraitValue, FlagsmithValue]>([
16+
[
17+
'int',
18+
{
19+
boolean_value: null,
20+
integer_value: 3,
21+
string_value: null,
22+
value_type: 'int',
23+
},
24+
3,
25+
],
26+
[
27+
'float',
28+
{
29+
boolean_value: null,
30+
float_value: 2.5,
31+
integer_value: null,
32+
string_value: null,
33+
value_type: 'float',
34+
},
35+
2.5,
36+
],
37+
[
38+
'bool',
39+
{
40+
boolean_value: false,
41+
integer_value: null,
42+
string_value: null,
43+
value_type: 'bool',
44+
},
45+
false,
46+
],
47+
[
48+
'unicode',
49+
{
50+
boolean_value: null,
51+
integer_value: null,
52+
string_value: 'power_users',
53+
value_type: 'unicode',
54+
},
55+
'power_users',
56+
],
57+
])('reads value_type on a core trait (%s)', (_label, trait, expected) => {
58+
expect(featureStateToValue(trait)).toBe(expected)
1759
})
1860

1961
it('normalises a missing int/float to null', () => {

frontend/common/utils/featureStateToValue.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { FeatureStateValue, FlagsmithValue } from 'common/types/responses'
1+
import type {
2+
FeatureStateValue,
3+
FlagsmithValue,
4+
TraitValue,
5+
} from 'common/types/responses'
26

37
/**
48
* Flattens a feature state (or core trait) value into its typed scalar.
@@ -10,7 +14,7 @@ import type { FeatureStateValue, FlagsmithValue } from 'common/types/responses'
1014
* their unit tests) don't pull the Flux stores in through `utils.tsx`.
1115
*/
1216
export function featureStateToValue(
13-
value: FlagsmithValue | FeatureStateValue | undefined,
17+
value: FlagsmithValue | FeatureStateValue | TraitValue | undefined,
1418
): FlagsmithValue {
1519
if (value === null || value === undefined) {
1620
return null
@@ -19,9 +23,7 @@ export function featureStateToValue(
1923
return value
2024
}
2125
// `value_type` is the type key on core traits; `type` on feature states.
22-
const type =
23-
(value as { value_type?: FeatureStateValue['type'] }).value_type ??
24-
value.type
26+
const type = 'value_type' in value ? value.value_type : value.type
2527
switch (type) {
2628
case 'bool':
2729
return value.boolean_value

frontend/common/utils/utils.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
ProjectFlag,
1313
SegmentCondition,
1414
Tag,
15+
TraitValue,
1516
UserPermissions,
1617
} from 'common/types/responses'
1718
import flagsmith from '@flagsmith/flagsmith'
@@ -892,7 +893,7 @@ const Utils = Object.assign({}, BaseUtils, {
892893
type: 'unicode',
893894
}
894895
},
895-
valueToTrait(value: FlagsmithValue) {
896+
valueToTrait(value: FlagsmithValue): TraitValue {
896897
const val = Utils.getTypedValue(value)
897898

898899
if (typeof val === 'boolean') {

0 commit comments

Comments
 (0)