#discriminated union magic

9 messages · Page 1 of 1 (latest)

upbeat hollow
#

I think i'm pretty close to getting a discriminated union working, but I've hit a snafu.

I'm getting an error of

 Type '{ type: "string"; length: number; default: string | (() => string); db: (prop: ...) => void; }' is not assignable to type 'never'.

on trying to call a function inside my discriminated union

type vingProp =
    | {
        type: 'string',
        length: number,
        default: string | (() => string),
        db: (prop: Extract<vingProp, { type: 'string' }>) => void,
    }
    | {
        type: 'number',
        min: number,
        max: number,
        default: number | (() => number),
        db: (prop: Extract<vingProp, { type: 'number' }>) => void,
    }

type vingSchema = {
  props : vingProp[],
}

const userSchema:vingSchema = {
  props : [
    {
      type : "string",
      length: 60,
      default: 'foo',
      db : (prop) => stringFunc(prop)
    },
     {
      type : "number",
      default: 5,
      min : 1,
      max: 5,
      db : (prop) => numberFunc(prop)
    }
  ]
}

function stringFunc(prop:Extract<vingProp, { type : "string"}>) { console.log(prop) }
function numberFunc(prop:Extract<vingProp, { type : "number"}>) { console.log(prop) }

const columns: Record<string, any> = {};
for (const prop of userSchema.props) {
  columns[prop.type] = prop.db(prop) // error is here
}
vital bobcatBOT
#
plainblackguy#8238

Preview:ts type vingProp = | { type: "string" length: number default: string | (() => string) db: ( prop: Extract<vingProp, {type: "string"}> ) => void } | { type: "number" min: number max: number d ...

upbeat hollow
#

have i defined my union wrong?

jade surge
#

!retsam19:correspondence-problem

vital bobcatBOT
#
Retsam19#2505
`!retsam19:correspondence-problem`:

There's a particular pattern that is safe but hard for the Typescript compiler to handle, which I call the "correspondence problem":

const functionsWithArguments = [
  { func: (arg: string) => {}, arg: "foo" },
  { func: (arg: number) => {}, arg: 0 },
];

for (const { func, arg } of functionsWithArguments) {
  func(arg);
//     ^^^
// Argument of type 'string | number' is not assignable to parameter of type 'never'.
//   Type 'string' is not assignable to type 'never'.
}

The problem is that func is typed as (x: string) => void | (x: number) => void and arg is string | number, but the compiler can't prove that they "correspond": that, for example, arg is only a string when func accepts strings.

As far as the type are concerned, arg could be number, and func could be (arg: string) => void, and that would be a type-error. It's easy for us to see that that won't happen, but that requires understanding the program at a higher-level than the level the compiler operates.

Depending on the specifics there's sometimes clever fixes, but usually I recommend using a type assertion and ignoring the issue:

func(arg as never);
jade surge
#

You can do the as never thing from the snippet, or this works - though it's annoying:

for (const prop of userSchema.props) {
  if(prop.type === "string") {
    columns[prop.type] = prop.db(prop)
  } else {
    columns[prop.type] = prop.db(prop)
  }
}
upbeat hollow
#

thank you, i'll give that a shot!

#

!close