Skip to content

Commit e2dd1cd

Browse files
committed
[New] add types
1 parent ac498a7 commit e2dd1cd

16 files changed

+160
-12
lines changed

.attw.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"pack": true,
3+
"ignoreRules": [
4+
"named-exports"
5+
]
6+
}

.eslintrc

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"rules": {
77
"eqeqeq": [2, "allow-null"],
88
"id-length": 0,
9+
"no-extra-parens": "off",
910
"no-invalid-this": "off",
1011
},
1112

auto.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
interface RegExp {
2+
readonly flags: typeof RegExp.prototype.flags;
3+
}
4+
5+
declare const x: never;
6+
export = x;

implementation.d.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
type D = 'd' | '';
2+
type G = 'g' | '';
3+
type I = 'i' | '';
4+
type M = 'm' | '';
5+
type S = 's' | '';
6+
type U = 'u' | '';
7+
type V = 'v' | '';
8+
type Y = 'y' | '';
9+
10+
declare namespace flags {
11+
type Flags = '' | `${D}${G}${I}${M}${S}${U}${V}${Y}`;
12+
}
13+
14+
declare function flags(
15+
this: RegExp | {
16+
__proto__?: unknown;
17+
hasIndices?: boolean;
18+
global?: boolean;
19+
ignoreCase?: boolean;
20+
multiline?: boolean;
21+
dotAll?: boolean;
22+
unicode?: boolean;
23+
unicodeSets?: boolean;
24+
sticky?: boolean;
25+
},
26+
): flags.Flags;
27+
28+
export = flags;

implementation.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ var $TypeError = require('es-errors/type');
55

66
var $Object = Object;
77

8-
module.exports = setFunctionName(function flags() {
8+
module.exports = setFunctionName(/** @type {import('./implementation')} */ function flags() {
99
if (this == null || this !== $Object(this)) {
1010
throw new $TypeError('RegExp.prototype.flags getter called on non-object');
1111
}
12+
1213
var result = '';
1314
if (this.hasIndices) {
1415
result += 'd';
@@ -34,6 +35,7 @@ module.exports = setFunctionName(function flags() {
3435
if (this.sticky) {
3536
result += 'y';
3637
}
37-
return result;
38+
// eslint-disable-next-line no-extra-parens
39+
return /** @type {ReturnType<flags>} */ (result);
3840
}, 'get flags', true);
3941

index.d.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import implementation = require('./implementation');
2+
import polyfill = require('./polyfill');
3+
import shim = require('./shim');
4+
5+
type Implementation = typeof implementation;
6+
type Polyfill = typeof polyfill;
7+
type Shim = typeof shim;
8+
9+
declare namespace index {
10+
type Flags = implementation.Flags;
11+
12+
const implementation: Implementation;
13+
const polyfill: Polyfill;
14+
const shim: Shim;
15+
}
16+
17+
declare function index(re: ThisParameterType<Implementation>): implementation.Flags;
18+
19+
export = index;

package.json

+11-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"tests-only": "nyc tape 'test/**/*.js'",
1919
"prelint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git')",
2020
"lint": "eslint --ext=js,mjs .",
21-
"postlint": "evalmd README.md && es-shim-api --bound",
21+
"postlint": "evalmd README.md && es-shim-api --bound && tsc && attw -P",
2222
"version": "auto-changelog && git add CHANGELOG.md",
2323
"postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
2424
},
@@ -48,8 +48,14 @@
4848
"set-function-name": "^2.0.2"
4949
},
5050
"devDependencies": {
51+
"@arethetypeswrong/cli": "^0.17.4",
5152
"@es-shims/api": "^2.5.1",
5253
"@ljharb/eslint-config": "^21.1.1",
54+
"@types/call-bind": "^1.0.5",
55+
"@types/define-properties": "^1.1.5",
56+
"@types/functions-have-names": "^1.2.2",
57+
"@types/object-inspect": "^1.13.0",
58+
"@types/tape": "^5.8.1",
5359
"auto-changelog": "^2.5.0",
5460
"available-regexp-flags": "^1.0.4",
5561
"eclint": "^2.8.1",
@@ -66,7 +72,8 @@
6672
"nyc": "^10.3.2",
6773
"object-inspect": "^1.13.4",
6874
"safe-publish-latest": "^2.0.0",
69-
"tape": "^5.9.0"
75+
"tape": "^5.9.0",
76+
"typescript": "next"
7077
},
7178
"testling": {
7279
"files": "test/index.js",
@@ -99,7 +106,8 @@
99106
},
100107
"publishConfig": {
101108
"ignore": [
102-
".github/workflows"
109+
".github/workflows",
110+
".attw.json"
103111
]
104112
}
105113
}

polyfill.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import implementation from "./implementation";
2+
3+
declare function polyfill(): typeof implementation;
4+
5+
export = polyfill;

polyfill.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var implementation = require('./implementation');
55
var supportsDescriptors = require('define-properties').supportsDescriptors;
66
var $gOPD = Object.getOwnPropertyDescriptor;
77

8+
/** @type {import('./polyfill.d.ts')} */
89
module.exports = function getPolyfill() {
910
if (supportsDescriptors && (/a/mig).flags === 'gim') {
1011
var descriptor = $gOPD(RegExp.prototype, 'flags');

shim.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import implementation from "./implementation";
2+
3+
declare function shim(): typeof implementation;
4+
5+
export = shim;
6+

shim.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ var $TypeError = require('es-errors');
88
var getProto = require('get-proto');
99
var regex = /a/;
1010

11+
/** @type {import('./shim.d.ts')} */
1112
module.exports = function shimFlags() {
12-
if (!supportsDescriptors || !getProto) {
13+
if (!supportsDescriptors || !getProto || !gOPD) {
1314
throw new $TypeError('RegExp.prototype.flags requires a true ES5 environment that supports property descriptors');
1415
}
1516
var polyfill = getPolyfill();
16-
var proto = getProto(regex);
17+
var proto = /** @type {typeof RegExp.prototype} */ (getProto(regex));
1718
var descriptor = gOPD(proto, 'flags');
1819
if (!descriptor || descriptor.get !== polyfill) {
1920
defineProperty(proto, 'flags', {

test/implementation.js

+2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ var runTests = require('./tests');
99
test('as a function', function (t) {
1010
t.test('bad array/this value', { skip: !hasStrictMode }, function (st) {
1111
/* eslint no-useless-call: 0 */
12+
// @ts-expect-error
1213
st['throws'](function () { flags.call(undefined); }, TypeError, 'undefined is not an object');
14+
// @ts-expect-error
1315
st['throws'](function () { flags.call(null); }, TypeError, 'null is not an object');
1416
st.end();
1517
});

test/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ var runTests = require('./tests');
66

77
test('as a function', function (t) {
88
t.test('bad array/this value', function (st) {
9+
// @ts-expect-error
910
st['throws'](function () { flags(undefined); }, TypeError, 'undefined is not an object');
11+
// @ts-expect-error
1012
st['throws'](function () { flags(null); }, TypeError, 'null is not an object');
1113
st.end();
1214
});

test/shimmed.js

+9
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ var runTests = require('./tests');
1616
test('shimmed', function (t) {
1717
var descriptor = Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags');
1818

19+
// @ts-expect-error
1920
t.equal(descriptor.get.length, 0, 'RegExp#flags getter has a length of 0');
2021

2122
t.test('Function name', { skip: !functionsHaveNames }, function (st) {
23+
// @ts-expect-error
2224
st.equal(descriptor.get.name, functionsHaveConfigurableNames ? 'get flags' : 'flags', 'RegExp#flags getter has name "get flags" (or "flags" if function names are not configurable)');
2325
st.end();
2426
});
@@ -29,19 +31,26 @@ test('shimmed', function (t) {
2931
});
3032

3133
t.test('bad array/this value', { skip: !hasStrictMode }, function (st) {
34+
// @ts-expect-error
3235
st['throws'](function () { return descriptor.get.call(undefined); }, TypeError, 'undefined is not an object');
36+
// @ts-expect-error
3337
st['throws'](function () { return descriptor.get.call(null); }, TypeError, 'null is not an object');
3438
st.end();
3539
});
3640

3741
t.test('has the correct descriptor', function (st) {
42+
// @ts-expect-error
3843
st.equal(descriptor.configurable, true);
44+
// @ts-expect-error
3945
st.equal(descriptor.enumerable, false);
46+
// @ts-expect-error
4047
st.equal(typeof descriptor.get, 'function');
48+
// @ts-expect-error
4149
st.equal(descriptor.set, undefined);
4250
st.end();
4351
});
4452

53+
// @ts-expect-error
4554
runTests(callBind(descriptor.get), t);
4655

4756
t.end();

test/tests.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ var regexProperties = require('available-regexp-flags/properties');
1111

1212
var sortedFlags = availableFlags.slice().sort(function (a, b) { return a.localeCompare(b); }).join('');
1313

14+
/** @type {(stringRegex: string) => RegExp} */
1415
var getRegexLiteral = function (stringRegex) {
15-
try {
16-
// eslint-disable-next-line no-new-func
17-
return Function('return ' + stringRegex + ';')();
18-
} catch (e) { /**/ }
19-
return null;
16+
// eslint-disable-next-line no-new-func
17+
return Function('return ' + stringRegex + ';')();
2018
};
2119

20+
/** @type {(flags: (re: ThisParameterType<typeof import('../implementation')>) => ReturnType<import('../implementation')>, t: import('tape').Test) => void} */
2221
module.exports = function runTests(flags, t) {
2322
forEach(v.primitives, function (nonObject) {
2423
t['throws'](
24+
// @ts-expect-error
2525
function () { flags(nonObject); },
2626
TypeError,
2727
'throws when called with a non-object receiver: ' + inspect(nonObject)
@@ -80,9 +80,11 @@ module.exports = function runTests(flags, t) {
8080
t.test('generic flags', function (st) {
8181
st.equal(flags({}), '');
8282
st.equal(flags({ ignoreCase: true }), 'i');
83+
// @ts-expect-error truthy values work, but the type says "boolean"
8384
st.equal(flags({ dotAll: 1, global: 0, sticky: 1, unicode: 1 }), 'suy');
8485
st.equal(flags({ __proto__: { multiline: true } }), 'm');
8586

87+
/** @type {Extract<Parameters<flags>[0], { __proto__?: unknown }>} */
8688
var obj = {};
8789
forEach(availableFlags, function (flag) {
8890
if (flag !== 'v') {
@@ -134,6 +136,7 @@ module.exports = function runTests(flags, t) {
134136
}
135137
});
136138

139+
// @ts-expect-error
137140
flags(re);
138141

139142
st.equal(calls, 'dgimsuy', 'getters are called in expected order');

tsconfig.json

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"compilerOptions": {
3+
/* Visit https://aka.ms/tsconfig.json to read more about this file */
4+
5+
/* Projects */
6+
7+
/* Language and Environment */
8+
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
9+
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
10+
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
11+
"useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
12+
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
13+
14+
/* Modules */
15+
"module": "commonjs", /* Specify what module code is generated. */
16+
// "rootDir": "./", /* Specify the root folder within your source files. */
17+
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
18+
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
19+
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
20+
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
21+
// "typeRoots": ["types"], /* Specify multiple folders that act like `./node_modules/@types`. */
22+
"resolveJsonModule": true, /* Enable importing .json files. */
23+
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
24+
25+
/* JavaScript Support */
26+
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
27+
"checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
28+
"maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
29+
30+
/* Emit */
31+
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
32+
"declarationMap": true, /* Create sourcemaps for d.ts files. */
33+
"noEmit": true, /* Disable emitting files from a compilation. */
34+
35+
/* Interop Constraints */
36+
"allowSyntheticDefaultImports": true, /* Allow `import x from y` when a module doesn't have a default export. */
37+
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
38+
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
39+
40+
/* Type Checking */
41+
"strict": true, /* Enable all strict type-checking options. */
42+
43+
/* Completeness */
44+
// "skipLibCheck": true /* Skip type checking all .d.ts files. */
45+
},
46+
"exclude": [
47+
"coverage",
48+
],
49+
}

0 commit comments

Comments
 (0)