A function that receive the current object instance as parameter and returns a Primitive.
Optional
args: RelationParameters<This, Args>The arguments to pass to the matching relation definition (see Primitive.RelationParameters)
The property decorator function.
In the following example let's imagine the DEFINITION
object is actually
bigger than it is.
The @Select
decorator is used to pick the Primitive type of the
decorated property with the help of a combination of key found in the
Protocol
instance.
class SubProtocol {
...
}
const DEFINITION = {
0: {
1: SubProtocol,
...
0xFF: undefined // Return undefined if shouldn't read anything
},
...
}
class Protocol {
@Uint8
foo: number
@Uint8
bar: number
@Select(_ => DEFINITION[_.foo][_.bar])
sub_protocol: any
}
A use case where the @Select
decorator is mandatory is when working with
recursive type definition. Because of the way circular dependencies works in
TS you can't reference statically a class that has not already been defined.
A workaround to this is to use lazy getter with the @Select
decorator.
class Element {
@Uint8
type: number
@Select(_ => ({
0x00: () => undefined,
0x01: () => Protocol,
0x02: () => PrimitiveSymbol.u8,
0x03: () => PrimitiveSymbol.u16,
0x04: () => PrimitiveSymbol.u32,
}[_.type]()))
data: any
}
class Protocol {
@Uint32
length: number
@Size('length')
@Relation(Element)
elements: Element[]
}
@Select
decorator works similarly to the Choice decorator but the function passed as argument directly returns the Primitive instead of declaring the condition.The
@Select
decorator should be used for a small subset of special cases, most of the time the@Choice
decorator fits the job perfectly. But sometimes certain format definitions have a long list of sub type you need match to a set of value and defining every available options in the@Choice
decorator is verbose.At the end of the day use the one you feel make your definition easier to read in your declaration, both are valid options.