Home:ALL Converter>Flow, how to type cast to a more specific type from mixed

Flow, how to type cast to a more specific type from mixed

Ask Time:2018-06-08T21:58:42         Author:robC

Json Formatter

The flow-typed libdef for express defines locals as an object with mixed values.
Specifying the actual value types results in the errors below... how should these specific types be annotated?

// From libdef
type Locals = {
  [name: string]: mixed;
}

// Populated data
const locals: Locals = {
  version: 1.2,
  resources: {
    a: "abc",
    b: "def"
  }
};

// Annotate the actual type
const version: number = locals.version; // Error: mixed is incompatible with number
const resources: {[string]: string} = locals.resources; // Error: mixed is incompatible with object type

Author:robC,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/50762310/flow-how-to-type-cast-to-a-more-specific-type-from-mixed
James Kraus :

One way is to refine the type of whatever you've recieved until it fits the shape you're looking for. Usually, I make a handful of basic refinement functions and use those to build up larger refinements.\n\n(Try)\n\n// From libdef\ntype Locals = {\n [name: string]: mixed;\n}\n\n// Populated data\nconst locals: Locals = {\n version: 1.2,\n resources: {\n a: \"abc\",\n b: \"def\"\n }\n};\n\n// The type you want\ntype MyLocals = {\n version: number,\n resources: {\n // maybe this one is a map? idk\n [string]: string\n }\n}\n\n// Some basic refinement functions\nconst refineString = (x: mixed): string => {\n if (typeof x === 'string') {\n return x \n }\n throw new Error(\"Not a string\")\n}\n\nconst refineNumber = (x: mixed): number => {\n if (typeof x === 'number') {\n return x \n }\n throw new Error(\"Not a number\")\n}\n\nconst refineObj = (x: mixed): {[string]: mixed} => {\n if (x instanceof Object) {\n return x \n }\n throw new Error(\"Not an object\")\n}\n\n// More type-specifc refinement functions\nconst refineResources = (x: mixed): $ElementType<MyLocals, 'resources'> => {\n const anObj = refineObj(x)\n return Object.keys(anObj)\n .reduce((acc, k) => Object.assign(acc, { [k]: refineString(anObj[k]) }), {})\n}\n\nconst refineMyLocals = (x: mixed): MyLocals => {\n const {version, resources} = refineObj(x)\n return {\n version: refineNumber(version),\n resources: refineResources(resources)\n }\n}\n\n// Now use them to assert a type\nconst myLocals: MyLocals = refineMyLocals(locals)\nconst version: number = myLocals.version;\nconst resources: {[string]: string} = myLocals.resources;\n\n\nAlternatively, if the libdef is in the flow-typed folder, just go in there and change the libdef. It will make that type specific to your project, but it might be the most effective way to handle it assuming you don't need the the [name: string]: mixed type somewhere else in your code.",
2018-06-08T15:07:56
yy