Add type-alias-readonlydeep rule and make ducks mostly immutable

This commit is contained in:
Jamie Kyle
2023-01-13 12:07:26 -08:00
committed by GitHub
parent 11ce3c3d59
commit c58a723f45
47 changed files with 1164 additions and 871 deletions

View File

@@ -0,0 +1,58 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
function isReadOnlyDeep(node, scope) {
if (node.type !== 'TSTypeReference') {
return false;
}
let reference = scope.references.find(reference => {
return reference.identifier === node.typeName;
});
let variable = reference.resolved;
if (variable == null) {
return false;
}
let defs = variable.defs;
if (defs.length !== 1) {
return false;
}
let [def] = defs;
return (
def.type === 'ImportBinding' &&
def.parent.type === 'ImportDeclaration' &&
def.parent.source.type === 'Literal' &&
def.parent.source.value === 'type-fest'
);
}
/** @type {import("eslint").Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
hasSuggestions: false,
fixable: false,
schema: [],
},
create(context) {
return {
TSTypeAliasDeclaration(node) {
let scope = context.getScope(node);
if (isReadOnlyDeep(node.typeAnnotation, scope)) {
return;
}
context.report({
node: node.id,
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
});
},
};
},
};

View File

@@ -0,0 +1,79 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
const rule = require('./type-alias-readonlydeep');
const RuleTester = require('eslint').RuleTester;
// avoid triggering mocha's global leak detection
require('@typescript-eslint/parser');
const ruleTester = new RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
});
ruleTester.run('type-alias-readonlydeep', rule, {
valid: [
{
code: `import type { ReadonlyDeep } from "type-fest"; type Foo = ReadonlyDeep<{}>`,
},
{
code: `import { ReadonlyDeep } from "type-fest"; type Foo = ReadonlyDeep<{}>`,
},
],
invalid: [
{
code: `type Foo = {}`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
{
code: `type Foo = Bar<{}>`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
{
code: `type Foo = ReadonlyDeep<{}>`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
{
code: `interface ReadonlyDeep<T> {}; type Foo = ReadonlyDeep<{}>`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
{
code: `import type { ReadonlyDeep } from "foo"; type Foo = ReadonlyDeep<{}>`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
],
});