0

How can I make the compiler understand that the return type of the parse method is linked to the input of the serialize method and it is dynamic?

type QUESTION = {
  parse: (value: string) => any
  serialize: (value: ReturnType<QUESTION['parse']>) => string
}

const idsQuestions: QUESTION[] = [
  {
    parse: (input) => input.split('\n'),
    serialize: (ids) => ids.join('\n') // expect ids to be interpreted as string[]
  },
  {
    parse: (input) => input.split('\n').reduce((acc, id) => ({
      ...acc,
      [id]: true,
    }), {}),
    serialize: (ids) => Object.keys(ids).join('\n') // expect ids to be interpreted as { [string] => boolean }
  }
]

// expect this to throw an error on build
const questionWithError: QUESTION = {
  parse: () => false, // return type is boolean
  serialize: (ids) => ids.join('\n') // but ids is not interpreted as boolean, so compiler doens't give an error
}

I've tried some uses of generics and unknown, but could not make it work. Tried to use infer also, but could not understand exactly how to work with it

I could have something hardcoded like this, but I wanted it to be dynamic

type QUESTION<T> = {
  parse: (value: string) => T
  serialize: (value: T) => string
}

const idsQuestions: [
  QUESTION<string[]>,
  QUESTION<{ [k: string]: boolean }>,
] = [
  {
    parse: (input) => input.split('\n'),
    serialize: (ids) => ids.join('\n') // expect ids to be interpreted as string[]
  },
  {
    parse: (input) => input.split('\n').reduce((acc, id) => ({
      ...acc,
      [id]: true,
    }), {}),
    serialize: (ids) => Object.keys(ids).join('\n') // expect ids to be interpreted as { [string] => boolean }
  }
]

Playground link

4
  • Couldn't you just make QUESTION<T> and then use T as parameter and T[] as return type for parse, and upside down for serialize? Commented Jul 1, 2022 at 14:32
  • Yes, this solves the problem. Edited my question to be closer to my use case Commented Jul 1, 2022 at 14:41
  • expect ids to be interpreted as { [string] => boolean } what do you mean it's not supposed to throw an error. Commented Jul 1, 2022 at 14:49
  • yeah this one is correct, but the () => false right below have an error Commented Jul 1, 2022 at 14:51

2 Answers 2

1

I don't think what you're trying to achieve is doable without some helper functions or making it excessively verbose. Make a function that could help determine what T should be.

type QUESTION<T> = {
  parse: (value: string) => T
  serialize: (value: T) => string
}

function QUESTION<T>(parse: (value: string) => T, serialize: (value: T) => string): QUESTION<T> {
  return { parse, serialize };
}

const idsQuestions = [
  QUESTION(
    (input) => input.split('\n'),
    (ids) => ids.join('\n') // expect ids to be interpreted as string[]
  ),
  QUESTION(
    (input) => input.split('\n').reduce((acc, id) => ({
      ...acc,
      [id]: true,
    }), {} as Record<string, boolean>),
    (ids) => Object.keys(ids).join('\n') // expect ids to be interpreted as { [string] => boolean }
  )
] as const;
Sign up to request clarification or add additional context in comments.

1 Comment

This is the way to do it.
0

You need to use genric-type.

With this feature, you can tie between Types dynamically

interface Question<T>  {
  parse: (value: string) => T
  serialize: (value: T) => string
}

const idsQuestion: Question<string[]> = {
  parse: (input) => input.split('\n'),
  serialize: (ids) => ids.join('\n') // expect ids to be interpreted as string[]
}

// expect this to throw an error on build
const questionWithError: Question<boolean> = {
  parse: () => false, // return type is boolean
  serialize: (ids) => ids.join('\n') // the transpiler will show an error
}

3 Comments

The real use case is a bit more complex than the example, so using generics this way solves this problem, but don't solve the case I have.
so ask your complex one
question edited

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.