Skip to content

Instantly share code, notes, and snippets.

@snewell92
Created April 3, 2023 20:20
Show Gist options
  • Save snewell92/a0406f3d58bb86d797e30f2034b1e996 to your computer and use it in GitHub Desktop.
Save snewell92/a0406f3d58bb86d797e30f2034b1e996 to your computer and use it in GitHub Desktop.
ChatGPT Prompt for overload issue
import cli, { InputQuestion, ListQuestion } from "inquirer";
interface TypeSafeInput<
TValidatedInput,
TName extends string
> extends InputQuestion<{ [name in TName]: TValidatedInput }> {
type: "input";
name: TName;
message: string;
typePredicate: (input: any) => input is TValidatedInput;
parse: (input: any) => TValidatedInput;
}
// We don't always need to do anything after the booleanp predicate is true,
// but other times we do need to parse the string to another type.
const identity = <T>(x: T) => x;
type SimpleObject<TKey extends string, TValue> = { [name in TKey]: TValue };
type SO<TK extends string, TV> = SimpleObject<TK, TV>;
// overloads of up to 4 inputs
export async function prompt<
T1, TN1 extends string,
T2, TN2 extends string,
T3, TN3 extends string,
T4, TN4 extends string
>(inputs: [
TypeSafeInput<T1, TN1>,
TypeSafeInput<T2, TN2>,
TypeSafeInput<T3, TN3>,
TypeSafeInput<T4, TN4>
]): Promise<SO<TN1, T1> & SO<TN2, T2> & SO<TN3, T3> & SO<TN4, T4>>;
export async function prompt<
T1, TN1 extends string,
T2, TN2 extends string,
T3, TN3 extends string,
>(inputs: [
TypeSafeInput<T1, TN1>,
TypeSafeInput<T2, TN2>,
TypeSafeInput<T3, TN3>
]): Promise<SO<TN1, T1> & SO<TN2, T2> & SO<TN3, T3>>;
export async function prompt<
T1, TN1 extends string,
T2, TN2 extends string,
>(inputs: [
TypeSafeInput<T1, TN1>,
TypeSafeInput<T2, TN2>
]): Promise<SO<TN1, T1> & SO<TN2, T2>>;
export async function prompt<T1, TN1 extends string>(inputs: [TypeSafeInput<T1, TN1>]): Promise<SO<TN1, T1>> {
const answers = await cli.prompt(inputs);
return inputs.reduce((accum, curr) => {
const parsedValue = curr.parse(answers[curr.name]);
accum[curr.name] = parsedValue;
return accum;
}, {} as SO<TN1, T1>);
}
@snewell92
Copy link
Author

tsc output:

src/cli.ts:51:23 - error TS2394: This overload signature is not compatible with its implementation signature.

51 export async function prompt<
                         ~~~~~~

  src/cli.ts:81:23
    81 export async function prompt(inputs: readonly [TypeSafeInput<any, any>]): Promise<ParseNameTypeObject<typeof inputs[number]>> {
                             ~~~~~~
    The implementation signature is declared here.


Found 1 error in src/cli.ts:51

@snewell92
Copy link
Author

The following is a transcription of my convo w/ ChatGPT about the above code and TS error:

[Sean] Can you help diagnose and troubleshoot TypeScript compilation errors in my code?

[ChatGPT] Of course! I'd be happy to help you diagnose and troubleshoot your TypeScript compilation errors. Please provide me with the specific error messages you're encountering, along with any relevant code snippets or files.

[Sean] I am trying to use overloads to provide better type inference, and it appears to work correctly when using the prompt function, but I get an error when compiling.

First, here is a snippet of my typescript code using TypeScript version 5.0.3 and the @types/inquirer typings for the inquirer package.

cli.js pasted in above code

The output from tsc which contains the compiler error is: pasted in above cli output

How should the code in cli.ts change so that it compiles without errors on TypeScript 5.0.3?

[ChatGPT] The error message you're seeing indicates that there is a mismatch between one of your function's overload signatures and its implementation signature. In this case, the issue appears to be with the implementation signature not being compatible with one of the overload signatures.

Specifically, the prompt function's implementation signature is:

export async function prompt(inputs: readonly [TypeSafeInput<any, any>]): Promise<ParseNameTypeObject<typeof inputs[number]>> {
  // implementation details
}

[ChatGPT] This implementation signature specifies a single parameter of type readonly [TypeSafeInput<any, any>], which is an array containing one or more TypeSafeInput objects. However, one of your overload signatures specifies that the function should take multiple parameters of type TypeSafeInput. This mismatch between the implementation signature and one of the overload signatures is what's causing the compiler error.

To fix this error, you should update the implementation signature to match the overload signatures. Specifically, you should change the implementation signature to accept a rest parameter of type TypeSafeInput<any, any>[], like this:

export async function prompt<T extends TypeSafeInput<any, any>>(...inputs: T[]): Promise<ParseNameTypeObject<T>> {
  // implementation details
}

[ChatGPT] This implementation signature accepts one or more parameters of type TypeSafeInput<any, any>, and the ... syntax indicates that these parameters should be collected into an array. The return type uses a conditional type ParseNameTypeObject which has the same functionality as your existing implementation.

After making this change, the TypeScript compiler should be able to successfully compile your code.

[Me] It works! Thanks. Do you know of anyway to simplify this code? Some ideas I had were making a type helper that could extract the type and name from TypeSafeInput but TypeScript appears to have a limitation when using a heterogeneous collection type with generics. Any simplification or suggestions would be most welcome.

[ChatGPT] I'm glad to hear that the changes I suggested worked for you!

As for simplifying the code further, one approach could be to create a type helper function that extracts the type and name information from a TypeSafeInput object. Here's an example implementation:

For some reason it used python in the code syntax? lol

type ExtractTypeAndName<T extends TypeSafeInput<any, any>> = [T['typePredicate'] extends (input: any) => input is infer U ? U : never, T['name']];

[ChatGPT] This type helper uses indexed access types to extract the typePredicate and name properties from the input type T, and then returns a tuple of the extracted types.

You could then use this type helper to simplify the implementation of your prompt function. Here's an example implementation:

It then used swift syntax highlighting?? Still emitting TS to xD

export async function prompt<T extends TypeSafeInput<any, any>>(...inputs: T[]): Promise<Record<ExtractTypeAndName<T>[1], ExtractTypeAndName<T>[0]>> {
  const answers = await cli.prompt(inputs);

  return inputs.reduce((accum, curr) => {
    const parsedValue = curr.parse(answers[curr.name]);
    accum[curr.name] = parsedValue;
    return accum;
  }, {} as Record<ExtractTypeAndName<T>[1], ExtractTypeAndName<T>[0]>);
}

[ChatGPT] This implementation uses the ... syntax to collect the input parameters into an array of type T[]. It then uses the ExtractTypeAndName type helper to extract the type and name information for each input parameter, and returns a Record type that maps each input parameter's name to its corresponding validated type.

Note that the return type Record<ExtractTypeAndName[1], ExtractTypeAndName[0]> may look a bit confusing. It is essentially a type that maps each input parameter's name (which is the second element of the tuple returned by ExtractTypeAndName) to its corresponding validated type (which is the first element of the tuple returned by ExtractTypeAndName).

I hope this helps to simplify your code further! Let me know if you have any other questions.

@snewell92
Copy link
Author

It's suggestions to simplify do not, but the original suggestion did help me fix the code with this adjustment:

-export async function prompt<T1, TN1 extends string>(inputs: [TypeSafeInput<T1, TN1>]): Promise<SO<TN1, T1>> {
+export async function prompt<T1, TN1 extends string>(inputs: TypeSafeInput<T1, TN1>[]): Promise<SO<TN1, T1>> {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment