【问题标题】:Typescript Regex - Named Capture Groups Breaks?Typescript Regex - 命名捕获组中断?
【发布时间】:2020-10-04 02:46:23
【问题描述】:

更新:根据@Jack Misteli 的建议添加了@babel/plugin-transform-named-capturing-groups-regex 以查看 Babel。

更新:: 使用Babel 转换代码,然后我在@Jack MisteliCSB forked 中进行了测试。这里的关键是当我尝试返回groups 时,它是null


我正在尝试了解破坏此正则表达式的 Typescript(我的配置、语言等)是什么。

具体来说,它似乎与我使用的命名捕获组有关,因为当我删除它们时,它会起作用。

详情:

原始 TS 文件:

/**
 *
 * @param {string} markdownLink A link in the form of [title](link description)
 * @returns {object} An object with three keys: title, link, description
 */
export function parseLink(markdownLink: string) {
    const pattern: RegExp = new RegExp(
        /^(\[(?<title>[^\]]*)?\]\((?<link>[A-Za-z0-9\:\/\.\- ]+)(?<description>\"(.+)\")?\))/
    )

    const match = markdownLink.match(pattern)
    const groups = match?.groups
    return groups
}

这转译为(添加 babel 插件后更新):

"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.parseLink = parseLink;

var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));

var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));

var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));

var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));

var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));

function _wrapRegExp(re, groups) { _wrapRegExp = function _wrapRegExp(re, groups) { return new BabelRegExp(re, undefined, groups); }; var _RegExp = (0, _wrapNativeSuper2["default"])(RegExp); var _super = RegExp.prototype; var _groups = new WeakMap(); function BabelRegExp(re, flags, groups) { var _this = _RegExp.call(this, re, flags); _groups.set(_this, groups || _groups.get(re)); return _this; } (0, _inherits2["default"])(BabelRegExp, _RegExp); BabelRegExp.prototype.exec = function (str) { var result = _super.exec.call(this, str); if (result) result.groups = buildGroups(result, this); return result; }; BabelRegExp.prototype[Symbol.replace] = function (str, substitution) { if (typeof substitution === "string") { var groups = _groups.get(this); return _super[Symbol.replace].call(this, str, substitution.replace(/\$<([^>]+)>/g, function (_, name) { return "$" + groups[name]; })); } else if (typeof substitution === "function") { var _this = this; return _super[Symbol.replace].call(this, str, function () { var args = []; args.push.apply(args, arguments); if ((0, _typeof2["default"])(args[args.length - 1]) !== "object") { args.push(buildGroups(args, _this)); } return substitution.apply(this, args); }); } else { return _super[Symbol.replace].call(this, str, substitution); } }; function buildGroups(result, re) { var g = _groups.get(re); return Object.keys(g).reduce(function (groups, name) { groups[name] = result[g[name]]; return groups; }, Object.create(null)); } return _wrapRegExp.apply(this, arguments); }

/**
 *
 * @param {string} markdownLink A link in the form of [title](link description)
 * @returns {object} An object with three keys: title, link, description
 */
function parseLink(markdownLink) {
  var pattern = new RegExp( /*#__PURE__*/_wrapRegExp(/^(\[([\0-\\\^-\uFFFF]*)?\]\(([ \.-:A-Za-z]+)("(.+)")?\))/, {
    title: 2,
    link: 3,
    description: 4
  }));
  var match = markdownLink.match(pattern);
  var groups = match === null || match === void 0 ? void 0 : match.groups;
  return groups;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ...fQ==

我的tsconfig 是:

{
    "compilerOptions": {

        /* Basic Options */
        "target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
        "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
        "sourceMap": true /* Generates corresponding '.map' file. */,
        "outDir": "dist" /* Redirect output structure to the directory. */,
        "composite": true /* Enable project compilation */,

        /* Strict Type-Checking Options */
        "strict": true /* Enable all strict type-checking options. */,

        /* Module Resolution Options */
        "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
        "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,

        /* Advanced Options */
        "skipLibCheck": true /* Skip type checking of declaration files. */,
        "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
        "resolveJsonModule": true
    },
    "include": ["./src"]
}

我的.babelrc

{
    "presets": ["@babel/preset-env", "@babel/preset-typescript"],
    "plugins": [
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-transform-runtime",
        "@babel/plugin-transform-named-capturing-groups-regex"
    ]
}

值得注意的是这两种模式:

  • 那个I wrote
    ^(\[(?<title>[^\]]*)?\]\((?<link>[A-Za-z0-9\:\/\.\- ]+)(?<description>\"(.+)\")?\))
    
  • 还有transpiled一个
    /^(\[([\0-\\\^-\uFFFF]*)?\]\(([ \.-:A-Za-z]+)("(.+)")?\))/
    

不要不要做同样的事情(参见测试链接)。

如果我删除了命名的捕获组,它确实似乎有效,但我现在很难理解为什么我有一个复制完整的捕获组。

在上图中,您可以看到我将原生 JS 版本与导入的转译版本(来自 dist 文件夹)进行比较。

左边的调试器显示parsed的值,这是分配给转译版本返回的变量。

所以 - 我的问题:

  1. 我需要做些什么来启用 TS 中的命名捕获组吗? 更新 和 TS 无关,而是 Babel。答案是使用@babel/plugin-transform-named-capturing-groups-regex
  2. 有什么我应该知道的以了解为什么我得到了一个用于转译版本的重复组吗?
  3. 有没有更好的方法来做到这一点? :)
  4. 我在配置命名捕获组插件时做错了什么,当它存在时它仍然不返回匹配项?

【问题讨论】:

  • 我不确定first link 是否与您发布的正则表达式匹配。
  • 哎呀!谢谢@zzzzBov!固定。

标签: typescript babeljs


【解决方案1】:

这不是 Typescript 问题,而是 Babel 问题。命名的正则表达式是 ES2018 中引入的 a recent feature。 因此,为了使用它,您必须确保正确配置了 babel。 话虽如此,我认为它设置正确。转译后创建的正则表达式模式,因为我们正在创建一个适合 Babel 的 _wrapRegExp 的模式(不确定它在内部是如何工作的)。所以你不能将模式复制粘贴到https://regex101.com。正则表达式引擎不同,因此相似的输入会导致不同的输出。

您显示的代码示例不一致(请注意您如何构建正则表达式,控制台日志未显示)。但是主要问题是您的屏幕截图中的日志与代码中显示的不同。

没有一个代码样本实际上与组匹配。所以为了进行适当的比较,我创建了一个只返回匹配数组的 CSB。看起来一切都是一样的:https://codesandbox.io/s/wizardly-bird-jm74k?file=/src/index.js。我为您的 Typescript 制作了一个 JS 版本,created a babelized version of that file,并使用您的 Typescript 转译版本返回匹配项。

以下测试字符串的所有结果都相同。好消息,我认为一切正常!

[test](http://google.com "hello")
[test](https://google.com "hello")
[test](google.com)
[](google.com)
[test](google.com "hello")

【讨论】:

  • ?但是在 CSB 中,您只是返回数组,而不是组,对吗? 期望的结果是能够看到一个组。另外 - 我相信你是对的,但我没有遵循我的例子中不一致的地方。很想更新以使其正确。再次感谢您的关注!
  • 是的,我正在返回匹配的值,因为在您的示例中,组是未定义的(参见屏幕截图)。因此,很难测试具有未定义返回值的函数的结果。我所指的不一致之处在于您构建正则表达式对象的方式在 Typescript 版本和 Javascript 版本中是不同的。 console.logs 实际上没有显示在屏幕截图的左侧。这在@zzzzBov 发表评论后得到修复,但操场上使用的正则表达式不正确。
  • 另外关于@babel/plugin-transform-named-capturing-groups-regex 我不确定这是不是答案。我在共享的 babel REPL 中使用它,但由于最初的正则表达式没有捕获组,我无法测试它是否对组有帮助。但是使用您的初始代码仅作为参考,无论有没有它,您都会得到完全相同的结果/匹配)。
  • 是的!完全明白。我认为令人困惑的是我喜欢 使用命名的捕获组。当我尝试使用该插件时,它似乎可以很好地解释正则表达式模式,但是,它 only 返回一个数组。指定的捕获组不存在。因此,当我尝试访问 .groups 时,我得到了 null。示例:codesandbox.io/s/throbbing-wildflower-vxj1q?file=/src/…(还使用 babel 转译链接更新了原始问题)
猜你喜欢
  • 2011-03-03
  • 2022-01-12
  • 2021-08-23
  • 1970-01-01
  • 2014-06-26
  • 2010-12-25
  • 1970-01-01
  • 2014-09-11
相关资源
最近更新 更多