0

So I have a type map of format { 'type': type } and want to use it in a generic function to "dynamically" determine the return type, but well it doesn't work.

Here's the code:

// Using type instead of interface. I believe it doesn't make any difference?
type SupportedPropertyTypes = {
  string: string,
  number: number,
  boolean: boolean,
  object: any
}

type PropertyType = keyof SupportedPropertyTypes

type PropertyDefinition<T extends PropertyType, N = false> = {
  type: T,
  nullable?: N,
  fallback?: PropertyValue<T,N> | (() => PropertyValue<T,N>)
}

type StorageConfig = {
  namespace: string,
  properties: {
    [key: string]: PropertyDefinition<PropertyType>
  }
}

type PropertyValue<T extends PropertyType, N = false> = SupportedPropertyTypes[T]
  | (() => SupportedPropertyTypes[T])
  | (N extends true ? null : never)

const config = {
  namespace: 'foo',
  properties: {
    foo: { type: 'string' },
    bar: { type: 'number', fallback: 1338 },
    baz: { type: 'boolean', nullable: true },
    complicatedThing: {
      type: 'object',
      nullable: true,
      fallback: () => ({
        foo: 'bar',
        bar: 'baz',
        arr: [1,2],
      })
    }
  }
}

function getFallback<TProps extends StorageConfig['properties'], T extends keyof TProps> (key: T, props: TProps) {
  return typeof props[key].fallback == 'function'
    ? props[key].fallback()
    : props[key].fallback
}

// Expected: const foo: { foo: string, bar: string, arr: number[] }
// Got: error
const foo = getFallback('complicatedThing', config.properties)

And here goes the error:

Argument of type '{ foo: { type: string; }; bar: { type: string; fallback: number; }; baz: { type: string; nullable: boolean; }; complicatedThing: { type: string; nullable: boolean; fallback: () => { foo: string; bar: string; arr: number[]; }; }; }' is not assignable to parameter of type '{ [key: string]: PropertyDefinition<keyof SupportedPropertyTypes, false>; }'.
  Property 'foo' is incompatible with index signature.
    Type '{ type: string; }' is not assignable to type 'PropertyDefinition<keyof SupportedPropertyTypes, false>'.
      Types of property 'type' are incompatible.
        Type 'string' is not assignable to type 'keyof SupportedPropertyTypes'.(2345)

Wrote this code based on solutions from here and here but I'm probably still missing something.

5
  • Hmm, there's a lot of stuff going on in that code, but as far as I can see, getFallback() doesn't need to care about it, and you could write it like this. Note that your implementation doesn't call the fallback method, so you should be expecting foo to be a function and not a value the function returns. If that's wrong you should edit the code to be a proper minimal reproducible example. Anyway, does this address the question sufficiently? If so I could write up an answer explaining; if not, what am I missing? Commented Apr 1, 2023 at 13:39
  • > Note that your implementation doesn't call the fallback method Updated the snippet, thanks. Commented Apr 1, 2023 at 14:34
  • So then this version would handle the different possible return types... again, without caring much about the types at the top. Does that work for you? If not, what specifically is wrong with it? Commented Apr 1, 2023 at 14:41
  • Correct me if I'm wrong, but your solution implies that fallback always exists which is not the case (see the PropertyDefinition type in my code snippet - fallback is optional). If you pass foo instead of complicatedThing it won't work. See here. The problem is we never know whether fallback is there or not - if there's no fallback, I just want undefined. Commented Apr 1, 2023 at 14:51
  • So we can make fallback optional as shown here then? Would that work now? Commented Apr 1, 2023 at 16:16

0

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.