typescriptgenerics

Typescript Generic and Non-Generic Version of Type 2 -Define VOID property?


To build off of the question found here: Generic and Non-Generic Version of Type

The question asks: "how to have a generic and non-generic version of a typescript type" like

export class ServiceResponse {}
export class ServiceResponse<T> extends ServiceResponse {}

The provided answer is use one class such as: export class ServiceResponse<T = void> {}.

But something is missing from the answer, and that is "how do you define a void property?" . Presumably the ServiceResponse class will have a property such as result: T. How then do you define a non-generic object?

const resp: ServiceResponse {
    result: __ // <------ result MUST be defined, but cannot be void, so what can it be?
}

I understand I can make result an optional (result?) or union (T | null) type, but then I do not see the point of using <T = void>, because I will need to falsy-check my result every time I use it.

My ideal answer is one that lets me create a non-generic ServiceResponse where I do not (and cannot) define a result, and create a generic ServiceResponse<TThing> where I MUST define result.

So this is 2 questions:

  1. how do I assign result when T is void?
  2. is my ideal answer possible?

EDIT: more info for @jcalz. doesn't work, but is minimum code. One question: how to make this work WITHOUT defining result as optional?

export interface ServiceResult<T = void> {
    result: T;
}
const genericResult: ServiceResult<string> = { result: "rad" }
const nonGenericResult: ServiceResult = { result: void }  /**** doesn't work ****/
function test(x: ServiceResult): void {console.log(x)}
test(genericResult); //**** doesn't work
test(nonGenericResult);

// jose ramirez's answer gets a little closer, but still not what I'm looking for because I still have to assign {} to result.
export interface ServiceResult2<T = {}> {
    result: T;
}
const genericResult2: ServiceResult2<string> = { result: "rad" }
const nonGenericResult2: ServiceResult2 = { result: {} /* works but sucks */ }
function test2(x: ServiceResult2): void { console.log(x) }
test2(genericResult2);
test2(nonGenericResult2);

Solution

  • Posting my own answer because people are already voting to close, and that's just not useful.

    The answer is: it doesn't appear possible. I can either make result optional:

    export interface ServiceResult<T> { result? T; }
    

    ... but that's not what I want, because I want the generic version to be guaranteed to have a T and the non-generic to never have a T.

    The other option is to give the generic and non-generic classes different names, and inherit one from the other.

    export interface ServiceResult1 {};
    export interface ServiceResult2<T> extends ServiceResult1 { result: T };
    

    Neither is optimal, so that answers both my questions.