流,如何从混合类型转换为更具体的类型

时间:2018-06-08 13:58:43

标签: javascript flowtype

用于表达的流式类型libdef将locals定义为具有mixed值的对象。
指定实际值类型会导致以下错误...应该如何注释这些特定类型?

// 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

2 个答案:

答案 0 :(得分:1)

看起来这里唯一的方法是添加refinement逻辑。

(option1|option2)

请注意,您也可以自行投射数据:

if (typeof locals.version === "number") {
   const version: number = locals.version;
}
if (locals.resources instanceof Object) {
  const resources: {[string]: string} = locals.resources;
}

或定义新副本:

const version: number = Number(locals.version);

答案 1 :(得分:1)

一种方法是改进你收到的任何类型,直到它符合你正在寻找的形状。通常,我会制作一些基本的细化函数,并使用它们来构建更大的细化。

Try

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

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

// The type you want
type MyLocals = {
  version: number,
  resources: {
    // maybe this one is a map? idk
    [string]: string
  }
}

// Some basic refinement functions
const refineString = (x: mixed): string => {
  if (typeof x === 'string') {
    return x 
  }
  throw new Error("Not a string")
}

const refineNumber = (x: mixed): number => {
  if (typeof x === 'number') {
    return x 
  }
  throw new Error("Not a number")
}

const refineObj = (x: mixed): {[string]: mixed} => {
  if (x instanceof Object) {
    return x 
  }
  throw new Error("Not an object")
}

// More type-specifc refinement functions
const refineResources = (x: mixed): $ElementType<MyLocals, 'resources'> => {
  const anObj = refineObj(x)
  return Object.keys(anObj)
               .reduce((acc, k) => Object.assign(acc, { [k]: refineString(anObj[k]) }), {})
}

const refineMyLocals = (x: mixed): MyLocals => {
  const {version, resources} = refineObj(x)
  return {
    version: refineNumber(version),
    resources: refineResources(resources)
  }
}

// Now use them to assert a type
const myLocals: MyLocals = refineMyLocals(locals)
const version: number = myLocals.version;
const resources: {[string]: string} = myLocals.resources;

或者,如果libdef位于flow-typed文件夹中,只需进入并更改libdef即可。它会使该类型特定于您的项目,但它可能是处理它的最有效方法,假设您不需要代码中其他位置的[name: string]: mixed类型。