You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
2.7 KiB
98 lines
2.7 KiB
2 years ago
|
\begin{figure}[H]
|
||
|
\begin{lstlisting}[language=JavaScript]
|
||
|
export type InputType = {
|
||
|
string: string,
|
||
|
'date-time': Date,
|
||
|
number: number,
|
||
|
checkbox: boolean,
|
||
|
};
|
||
|
|
||
|
export type InputField<T = unknown> = {
|
||
|
defaultValue?: T;
|
||
|
validate?: (value: T) => boolean;
|
||
|
label?: string;
|
||
|
inputType: keyof InputType;
|
||
|
};
|
||
|
|
||
|
export class InputFieldBuilder {
|
||
|
static from<K extends keyof InputType>(inputType: K): InputField<InputType[K]> {
|
||
|
// ...
|
||
|
}
|
||
|
// ...
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Represents an action that can be performed on the target device
|
||
|
**/
|
||
|
export abstract class Action<TInput, TOutput> {
|
||
|
static module: string;
|
||
|
static action: string;
|
||
|
|
||
|
abstract run(params: TInput): Promise<TOutput>;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the `Params` generic type in `Action<Params, Result>`
|
||
|
**/
|
||
|
export type ExtractActionParams<TAction extends Action<unknown, unknown>> =
|
||
|
TAction extends Action<infer Params, unknown> ? Params : never;
|
||
|
\end{lstlisting}
|
||
|
\caption{Exemple de types avancés en TypeScript (1/2)}
|
||
|
\label{types}
|
||
|
\end{figure}
|
||
|
\begin{figure}[H]
|
||
|
\begin{lstlisting}[language=JavaScript]
|
||
|
/**
|
||
|
* This type returns a narrowed-down version of `Record<string, InputField>`, that requires that every value in `Params`
|
||
|
* has a field attached to it.
|
||
|
**/
|
||
|
export type MapActionParams<Params> = Params extends undefined | void | null
|
||
|
? unknown
|
||
|
: {
|
||
|
[Key in keyof Params]: Params[Key] extends InputType[keyof InputType]
|
||
|
? InputField<Params[Key]>
|
||
|
: InputField;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Conditionally sets the `fields` property of `Request` to:
|
||
|
* - if `TAction` is set to `unknown` (default), an optional `Record<string, InputField>`
|
||
|
* - if `TAction` is set to an `Action`, then it will extract the properties defined in that `Action`,
|
||
|
* and require you to properly define their fields
|
||
|
**/
|
||
|
export type RequestFields<TAction> = TAction extends Action<unknown, unknown>
|
||
|
? {
|
||
|
fields: Record<string, InputField> & MapActionParams<ExtractActionParams<TAction>>;
|
||
|
}
|
||
|
: {
|
||
|
fields?: Record<string, InputField>;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Represents a network request that will be sent to the device to perform the similarly-named Action.
|
||
|
**/
|
||
|
export type Request<TAction = unknown> = {
|
||
|
action?: string;
|
||
|
module?: string;
|
||
|
} & RequestFields<TAction>;
|
||
|
|
||
|
// Exemple:
|
||
|
|
||
|
class ShowMessageAction extends Action<{ message: string}, void> {
|
||
|
run({message}): Promise<void> {
|
||
|
// show `message`
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const ShowMessageRequestFields: RequestFields<ShowMessageAction> = {
|
||
|
module: 'system',
|
||
|
action: 'showMessage',
|
||
|
fields: {
|
||
|
// Si on omet `message` ou si on utilise un autre type, une erreur est affichée
|
||
|
message: InputFieldBuilder.from('string').build()
|
||
|
}
|
||
|
};
|
||
|
\end{lstlisting}
|
||
|
\caption{Exemple de types avancés en TypeScript (2/2)}
|
||
|
\end{figure}
|