如何将某些参数映射为可选参数?

时间:2018-01-03 16:01:37

标签: javascript typescript mapped-types

上下文:

我正在尝试编写一个函数,允许函数的用户使用 no typescript type assertions (只是普通的旧javascript语法)定义某种类型。基本上,我正在尝试编写像React的PropTypes之类的内容,但我想将使用这些“PropTypes”定义的类型映射到可以使用typescript强制执行的类型。

通过查看代码,可能更容易理解我想要做的事情。低于defineFunction的函数返回一个函数,该函数接收由“PropTypes”定义的对象。

interface PropType<T> { isOptional: PropType<T | undefined> }
type V<Props> = {[K in keyof Props]: PropType<Props[K]>};
interface PropTypes {
  string: PropType<string>,
  number: PropType<number>,
  bool: PropType<boolean>,
  shape: <R>(definer: (types: PropTypes) => V<R>) => PropType<R>
}

// the only purpose of this function is to capture the type and map it appropriately
// it does do anything else
function defineFunction<Props, R>(props: (types: PropTypes) => V<Props>, func: (prop: Props) => R) {
  return func;
}

// define a function
const myFunction = defineFunction(
  // this is the interface definition that will be mapped
  // to the actual required props of the function
  types => ({
    stringParam: types.string,
    optionalNumberParam: types.number.isOptional,
    objectParam: types.shape(types => ({
      nestedParam: types.string,
    }))
  }),
  // this is the function definition. the type of `props`
  // is the result of the mapped type above
  props => {
    props.objectParam.nestedParam
    return props.stringParam;
  }
);

// use function
myFunction({
  stringParam: '',
  optionalNumberParam: 0,
  objectParam: {
    nestedParam: ''
  }
});

以下是将鼠标悬停在VS代码中的类型上找到的myFunction的结果类型:

const myFunction: (prop: {
    stringParam: string;
    optionalNumberParam: number | undefined;
    objectParam: {
        nestedParam: string;
    };
}) => string

问题:

上述代码存在问题 - optionalNumberParam被正确定义为number | undefined,但实际上并不是可选的!

如果我省略optionalNumberParam,那么打字稿编译器就会对我大喊大叫。

enter image description here

无论如何断言类型是可选的而不仅仅是T | undefined

回复cale_b的评论:

  

只是一个想法 - 你试过OptionalNumberParam?:types.number.isOptional

是的,语法无效:

enter image description here

为了澄清,这个defineFunction应该让用户使用 no typescript type assertions 定义一个类型 - 而是应该使用映射类型来推断所有内容。 ?只是打字稿。我正在尝试编写这个函数,以便 - 理论上 - javascript用户可以定义proptypes并且仍然使用typescript编译器强制执行这些proptypes。

1 个答案:

答案 0 :(得分:1)

因此,我能做的最接近的解决方法涉及我提到的双对象文字解决方案:

interface PropType<T> { type: T }

首先我删除了isOptional,因为它没有用,第二次我添加了一个T的属性,因为TypeScript不一定能说出PropType<T>和{{之间的区别1}}如果PropType<U>T不同,除非它们在结构上不同。我认为您不需要使用U属性。

然后我没碰到一些东西:

type

现在,我正在创建函数type V<Props> = {[K in keyof Props]: PropType<Props[K]>}; interface PropTypes { string: PropType<string>, number: PropType<number>, bool: PropType<boolean>, shape: <R>(definer: (types: PropTypes) => V<R>) => PropType<R> } function defineFunction<Props, R>(props: (types: PropTypes) => V<Props>, func: (prop: Props) => R) { return func; } ,它接受​​类型withPartialR的两个参数,并返回类型O的值。

R & Partial<O>

让我们试一试:

function withPartial<R, O>(required: R, optional: O): R & Partial<O> {
  return Object.assign({}, required, optional);
}

请注意我如何将原始对象文字拆分为两个:一个具有必需属性,另一个具有可选属性,并使用const myFunction = defineFunction( types => withPartial( { stringParam: types.string, objectParam: types.shape(types => ({ nestedParam: types.string, })) }, { optionalNumberParam: types.number } ), props => { props.objectParam.nestedParam return props.stringParam; } ); 函数重新组合它们。另请注意withPartial的用户不需要使用任何特定于TypeScript的表示法,我认为这是您的要求之一。

检查withPartial()的类型会给您:

myFunction

这就是你想要的。观察:

const myFunction: (
  prop: {
    stringParam: string;
    objectParam: {
      nestedParam: string;
    };
    optionalNumberParam?: number;
  }
) => string

希望有所帮助;祝你好运!