函数范式的空对象模式

时间:2018-03-09 09:54:28

标签: typescript functional-programming null-object-pattern

对于Angular ngrx应用程序使用Typescript,我必须做很多检查,我访问或传递的值是null / undefined,例如在可能存在的列表上调用.map()。

要走的路是什么?确保整个Codebase nevererver移交null?在每次调用之前检查,以防生成像空列表这样的空对象?或者是否有某种类型的null-object模式用于函数式编程?

帕维尔的@Comment: 我们有一个可以选择的项目列表,如果是这样,详细信息将在视图中展开。在我看来,当删除列表中已打开的项目时,应用程序崩溃了,因为对已打开项目的引用设置为null。我通过对我编写的所有选择器进行空检查来修复此问题,以便修复应用程序崩溃,正如我在许多其他选择器中看到的那样,他们在浏览器中使用应用程序本地状态。

1 个答案:

答案 0 :(得分:3)

这里有一些众所周知的模式可以提供帮助。

选项类型,又名可能

Maybe / Option是一种在TypeScript中具有以下结构的和类型:

type Maybe<T> = T | void;

您可以在typescript user manual中了解有关实施该问题的更多信息,只需ctrl-f即可查看&#39;因为他们似乎出于某种原因避开了片段标识符。

装修

Maybe(其中一个反正)的问题是它很快变成了病毒,它接管了整个代码库,每个值都变成了一个Maybe。如果没有模式匹配,处理这一点尤其烦人(打字稿手册中的例子非常详细)。一个更好的选择恕我直言,因为JavaScript / TypeScript缺少任何类型的存在运算符是编写没有空检查的函数,然后使用执行检查的函数来装饰它们。

function argCheckDecorator(f) {
  return function(...args) {
    // Functions have a length property that is their arity.
    // You could modify this to only check the first argument,
    // not check arity for varargs, etc.
    if (
      args.length === f.length &&
      args.every(arg => arg !== null && arg !== undefined)
    ) {
      return f.apply(this, args);
    } else {
      // optionally warn console
      // console.warn(`Called ${f.name} with invalid arguments ${args}.`);
      return null;
    }
  }
}https://www.destroyallsoftware.com/talks/boundaries

这里显而易见的问题是你正在进行相当广泛的运行时检查(首先是AFAIK导致你提出问题的原因),而不是依靠编译器。不幸的是,在JavaScript / TypeScript中,您的选项总是有限的,没有办法保证对DOM的调用永远不会返回null或者对象属性访问永远不会返回{{1} (至少如果你正在解析一个对象的JSON HTTP响应)。

至少使用函数装饰器,您可以将每个函数的空值检查移出。将我的高阶函数转换为TypeScript装饰器留给读者练习。

更新

举一个例子来说明我在评论中谈到的内容:

undefined

同时,回到牧场......

// messy-shell.js
// All DOM mutation, AJAX calls, optional params, null checks, etc.
// go here.
import * as ideal from './perfect-world.js';
const getDOMElement = (selector, element=document) => {
  return (selector && element instanceof HTMLElement) ?
    ideal.getDOMElement(selector, element) : null;
};

const getAJAXData => (url, method='GET', params) => {
  let p = params ? // IRL you'll want to do more checking than this
    fetch(url, { body: params, method }) :
    fetch(url, { method });

  return p.then(resp => {
    if (resp.statusCode >= 200 && resp.statusCode < 400) {
      return resp.json();
    } else {
      throw new Error(`${resp.statusCode} response.`);
    }
  }).then(data => {
    if (data && (data.length || Object.keys(data).length)) {
      return ideal.processAJAXData(data);
    } else {
      throw new Error('Empty data response.');
    }
  }).then(processedData => {
    // update DOM here, or skip this and just return Promise<processedData>
  }).catch(err => {
    // do error handling
  });
};

现在你不必为每个小功能都想要这个级别的仪式,但是如果你的团队/代码库/问题领域足够大,你可能会。一些不错的属性不属于此:

  1. 理想世界中的东西很容易测试。
  2. 理想世界中的东西可以很容易地进行静态分析。
  3. 理想世界中的东西整洁,大部分都是自我记录的
  4. 您可能还想检查this