There are multiple ways to achieve the desired functionality.
Here are a couple of options to consider:
1) Incorporate Union Type
interface Action1<T> {
readonly type: string;
readonly payload: T;
}
interface Action2 {
readonly type: string;
}
type Action<T = never> = Action1<T> | Action2
// Ensure that payload is included
const actionWithPayload: Action1<string> = { type: 'action_name', payload: 'action payload' }; // successful
const actionWithPayload2: Action1<string> = { type: 'action_name' }; // error
const actionWithoutPayload: Action = { type: 'action_name' } // successful
const actionWithoutPayload2: Action = { type: 'action_name', payload: 1 } // error
2) Utilize Conditional Type
type IsAny<T> = 0 extends (1 & T) ? true : false;
type Action<T = any> = IsAny<T> extends true ? {
readonly type: string;
} : {
readonly type: string;
readonly payload: T;
}
// Ensuring payload presence
const actionWithPayload: Action<string> = { type:'action_name', payload: 'action payload' }; // ok
const actionWithPayload2: Action<any> = { type: 'action_name', payload: 'action payload' }; //error
// Making payload optional
const actionWithoutPayload: Action = { type: 'action_name' } // successfully executed
const actionWithoutPayload2: Action = { type: 'action_name', payload: 42 }// error