繁体   English   中英

TypeScript:是否可以限制箭头函数参数返回类型?

[英]TypeScript: Is it possible to restrict arrow function parameter return type?

我有一个 typescript 函数,它接受两个参数,一个配置object和一个function

function executeMaybe<Input, Output> (
  config: { percent: number },
  fn: (i: Input) => Output
): (i: Input) => Output | 'maybe next time 😛' {

  return function(input: Input) {
    return Math.random() < config.percent / 100 
      ? fn(input)
      : 'maybe next time 😛';
  };
}

该函数的用法如下所示:

interface Foo { number: number; }
interface Bar { string: string; }

const makeBarMaybe = executeMaybe<Foo, Bar>(
  { percent: 75 },
  foo => ({ string: `derived from Foo: ${JSON.stringify(foo)}` })
);

如果我尝试向我的配置对象文字添加一个不存在的属性,TypeScript 会通知我:

'{百分比:数字; baz:字符串; }' 不能分配给类型为 '{ percent: number; 的参数; }'。 对象字面量只能指定已知的属性,而 'baz' 不存在于类型 '{ percent: number; }'。 (2345)

但是,当我向从函数参数返回的对象添加附加属性时,没有提到错误:

截屏

在这种情况下是否可以让 TS 提供错误而不显式提供函数的返回类型?

另一个截图

这是我在玩这个的StackBlitz

您看到此错误是因为extra-property-checks 文字对象的处理方式不同。

对象文字在将它们分配给其他变量或将它们作为参数传递时会得到特殊处理并进行额外的属性检查。 如果对象字面量具有“目标类型”没有的任何属性,您将收到错误消息:

如果你仍然想为你的参数添加更多额外的属性,你应该期待Foo的子类型而不是超类型。 我的意思是,你的论点应该extend Foo而不是Foo

您需要重写您的executeMaybe并应用一个约束,其中callback函数需要Input子类型而不是Input本身:

function executeMaybe<Input, Output>(
  config: { percent: number },
  fn: <Inp extends Input>(i: Inp) => Output
): <Inp extends Input>(i: Inp) => Output | 'maybe next time 😛' {
  return function (input: Input) {
    const result =
      Math.random() < config.percent / 100 ? fn(input) : 'maybe next time 😛';

    console.log(result);

    return result;
  };
}

全码:

function executeMaybe<Input, Output>(
  config: { percent: number },
  fn: <Inp extends Input>(i: Inp) => Output
): <Inp extends Input>(i: Inp) => Output | 'maybe next time 😛' {
  return function (input: Input) {
    const result =
      Math.random() < config.percent / 100 ? fn(input) : 'maybe next time 😛';

    console.log(result);

    return result;
  };
}

console.clear();

interface Foo {
  number: number;
}

interface Bar {
  string: string;
}

const makeBarMaybe = executeMaybe<Foo, Bar>({ percent: 75 }, (foo) => ({
  string: `derived from Foo: ${JSON.stringify(foo)}`,
}));

const explicitFoo: Foo = { number: 10 };
const implicitFoo = { number: 20 };
const superFoo = { number: 30, extra: 'super' };

makeBarMaybe(explicitFoo);
makeBarMaybe(implicitFoo);
makeBarMaybe(superFoo); // same object | duck typing allows
makeBarMaybe({ number: 30, extra: 'super' }); // ok

操场

更新

我的不好,可能没看懂问题。

TypeScript 不支持精确类型,但有一个解决方法:

type PseudoExact<T, U> = T extends U ? U extends T ? U : never : never

function executeMaybe<Input, Output>(
  config: { percent: number },
  fn: <Inp extends Input>(i: PseudoExact<Input, Inp>) => Output
): <Inp extends Input>(i: PseudoExact<Input, Inp>) => Output | 'maybe next time 😛' {
  return function <Inp extends Input>(input: PseudoExact<Input, Inp>) {
    const result =
      Math.random() < config.percent / 100 ? fn(input) : 'maybe next time 😛';

    console.log(result);

    return result;
  };
}

PseudoExact - 检查超类型是否扩展了子类型。

具有预期错误的完整代码:

type PseudoExact<T, U> = T extends U ? U extends T ? U : never : never

function executeMaybe<Input, Output>(
  config: { percent: number },
  fn: <Inp extends Input>(i: PseudoExact<Input, Inp>) => Output
): <Inp extends Input>(i: PseudoExact<Input, Inp>) => Output | 'maybe next time 😛' {
  return function <Inp extends Input>(input: PseudoExact<Input, Inp>) {
    const result =
      Math.random() < config.percent / 100 ? fn(input) : 'maybe next time 😛';

    console.log(result);

    return result;
  };
}

console.clear();

interface Foo {
  number: number;
}

interface Bar {
  string: string;
}

const makeBarMaybe = executeMaybe<Foo, Bar>({ percent: 75 }, (foo) => ({
  string: `derived from Foo: ${JSON.stringify(foo)}`,
}));

const explicitFoo: Foo = { number: 10 };
const implicitFoo = { number: 20 };
const superFoo = { number: 30, extra: 'super' };

makeBarMaybe(explicitFoo);
makeBarMaybe(implicitFoo);
makeBarMaybe(superFoo); // expected error
makeBarMaybe({ number: 30, extra: 'super' }); // expected error

操场

您可以使用更高级的类型实用程序,而不是我天真的PseudoExact 请看这条评论

export type Equals<X, Y> =
    (<T>() => T extends X ? 1 : 2) extends
    (<T>() => T extends Y ? 1 : 2) ? true : false;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM