📄 check-modifiers¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 10 |
| 📦 Imports | 7 |
| 📊 Variables & Constants | 1 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/typescript-estree/src/check-modifiers.ts
📦 Imports¶
| Name | Source |
|---|---|
TSNode |
./ts-estree |
getDecorators |
./getModifiers |
getModifiers |
./getModifiers |
getDeclarationKind |
./node-utils |
hasModifier |
./node-utils |
isThisIdentifier |
./node-utils |
createError |
./node-utils |
Variables & Constants¶
| Name | Type | Kind | Value | Exported |
|---|---|---|---|---|
SyntaxKind |
any |
const | ts.SyntaxKind |
✗ |
Functions¶
checkModifiers(node: ts.Node): void¶
Parameters:
nodets.Node
Returns: void
Calls:
nodeHasIllegalDecoratorscreateError (from ./node-utils)getDecorators (from ./getModifiers)nodeCanBeDecoratedts.isMethodDeclarationnodeIsPresentgetModifiers (from ./getModifiers)ts.tokenToStringts.isClassLikets.isInterfaceDeclarationts.isTypeAliasDeclarationts.isPropertyDeclarationts.isVariableStatementgetDeclarationKind (from ./node-utils)getContainingFunctioncheckObjectPropertyModifierts.isDecoratormodifiers.includes
Internal Comments:
// typescript<5.0.0
/* includeIllegalDecorators */
// `checkGrammarModifiers` function in typescript
// `checkGrammarAsyncModifier` function in `typescript`
// `checkGrammarModifiers` function in `typescript` (x2)
// `checkParameter` function in `typescript`
// In `typescript` package, it's `ts.hasSyntacticModifier(node, ts.ModifierFlags.ParameterPropertyModifier)`
// https://github.com/typescript-eslint/typescript-eslint/pull/6615#discussion_r1136489935
// `ts.getModifiers()` can't access invalid modifiers on object properties
// Eg: `({declare a: 1})`
// See https://github.com/typescript-eslint/typescript-eslint/pull/11931#discussion_r2678961730
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- incorrect type
// @ts-expect-error intentional to access deprecated `node.modifiers`
Code
export function checkModifiers(node: ts.Node): void {
// typescript<5.0.0
if (nodeHasIllegalDecorators(node)) {
throw createError(
node.illegalDecorators[0],
'Decorators are not valid here.',
);
}
for (const decorator of getDecorators(
node,
/* includeIllegalDecorators */ true,
) ?? []) {
// `checkGrammarModifiers` function in typescript
if (!nodeCanBeDecorated(node as TSNode)) {
if (ts.isMethodDeclaration(node) && !nodeIsPresent(node.body)) {
throw createError(
decorator,
'A decorator can only decorate a method implementation, not an overload.',
);
} else {
throw createError(decorator, 'Decorators are not valid here.');
}
}
}
const modifiers =
getModifiers(node, /* includeIllegalModifiers */ true) ?? [];
for (const modifier of modifiers) {
if (modifier.kind !== SyntaxKind.ReadonlyKeyword) {
if (
node.kind === SyntaxKind.PropertySignature ||
node.kind === SyntaxKind.MethodSignature
) {
throw createError(
modifier,
`'${ts.tokenToString(
modifier.kind,
)}' modifier cannot appear on a type member`,
);
}
if (
node.kind === SyntaxKind.IndexSignature &&
(modifier.kind !== SyntaxKind.StaticKeyword ||
!ts.isClassLike(node.parent))
) {
throw createError(
modifier,
`'${ts.tokenToString(
modifier.kind,
)}' modifier cannot appear on an index signature`,
);
}
}
if (
modifier.kind !== SyntaxKind.InKeyword &&
modifier.kind !== SyntaxKind.OutKeyword &&
modifier.kind !== SyntaxKind.ConstKeyword &&
node.kind === SyntaxKind.TypeParameter
) {
throw createError(
modifier,
`'${ts.tokenToString(
modifier.kind,
)}' modifier cannot appear on a type parameter`,
);
}
if (
(modifier.kind === SyntaxKind.InKeyword ||
modifier.kind === SyntaxKind.OutKeyword) &&
(node.kind !== SyntaxKind.TypeParameter ||
!(
ts.isInterfaceDeclaration(node.parent) ||
ts.isClassLike(node.parent) ||
ts.isTypeAliasDeclaration(node.parent)
))
) {
throw createError(
modifier,
`'${ts.tokenToString(
modifier.kind,
)}' modifier can only appear on a type parameter of a class, interface or type alias`,
);
}
if (
modifier.kind === SyntaxKind.ReadonlyKeyword &&
node.kind !== SyntaxKind.PropertyDeclaration &&
node.kind !== SyntaxKind.PropertySignature &&
node.kind !== SyntaxKind.IndexSignature &&
node.kind !== SyntaxKind.Parameter
) {
throw createError(
modifier,
"'readonly' modifier can only appear on a property declaration or index signature.",
);
}
if (
modifier.kind === SyntaxKind.DeclareKeyword &&
ts.isClassLike(node.parent) &&
!ts.isPropertyDeclaration(node)
) {
throw createError(
modifier,
`'${ts.tokenToString(
modifier.kind,
)}' modifier cannot appear on class elements of this kind.`,
);
}
if (
modifier.kind === SyntaxKind.DeclareKeyword &&
ts.isVariableStatement(node)
) {
const declarationKind = getDeclarationKind(node.declarationList);
if (declarationKind === 'using' || declarationKind === 'await using') {
throw createError(
modifier,
`'declare' modifier cannot appear on a '${declarationKind}' declaration.`,
);
}
}
if (
modifier.kind === SyntaxKind.AbstractKeyword &&
node.kind !== SyntaxKind.ClassDeclaration &&
node.kind !== SyntaxKind.ConstructorType &&
node.kind !== SyntaxKind.MethodDeclaration &&
node.kind !== SyntaxKind.PropertyDeclaration &&
node.kind !== SyntaxKind.GetAccessor &&
node.kind !== SyntaxKind.SetAccessor
) {
throw createError(
modifier,
`'${ts.tokenToString(
modifier.kind,
)}' modifier can only appear on a class, method, or property declaration.`,
);
}
if (
(modifier.kind === SyntaxKind.StaticKeyword ||
modifier.kind === SyntaxKind.PublicKeyword ||
modifier.kind === SyntaxKind.ProtectedKeyword ||
modifier.kind === SyntaxKind.PrivateKeyword) &&
(node.parent.kind === SyntaxKind.ModuleBlock ||
node.parent.kind === SyntaxKind.SourceFile)
) {
throw createError(
modifier,
`'${ts.tokenToString(
modifier.kind,
)}' modifier cannot appear on a module or namespace element.`,
);
}
if (
modifier.kind === SyntaxKind.AccessorKeyword &&
node.kind !== SyntaxKind.PropertyDeclaration
) {
throw createError(
modifier,
"'accessor' modifier can only appear on a property declaration.",
);
}
// `checkGrammarAsyncModifier` function in `typescript`
if (
modifier.kind === SyntaxKind.AsyncKeyword &&
node.kind !== SyntaxKind.MethodDeclaration &&
node.kind !== SyntaxKind.FunctionDeclaration &&
node.kind !== SyntaxKind.FunctionExpression &&
node.kind !== SyntaxKind.ArrowFunction
) {
throw createError(modifier, "'async' modifier cannot be used here.");
}
// `checkGrammarModifiers` function in `typescript`
if (
node.kind === SyntaxKind.Parameter &&
(modifier.kind === SyntaxKind.StaticKeyword ||
modifier.kind === SyntaxKind.ExportKeyword ||
modifier.kind === SyntaxKind.DeclareKeyword ||
modifier.kind === SyntaxKind.AsyncKeyword)
) {
throw createError(
modifier,
`'${ts.tokenToString(
modifier.kind,
)}' modifier cannot appear on a parameter.`,
);
}
// `checkGrammarModifiers` function in `typescript`
if (
modifier.kind === SyntaxKind.PublicKeyword ||
modifier.kind === SyntaxKind.ProtectedKeyword ||
modifier.kind === SyntaxKind.PrivateKeyword
) {
for (const anotherModifier of modifiers) {
if (
anotherModifier !== modifier &&
(anotherModifier.kind === SyntaxKind.PublicKeyword ||
anotherModifier.kind === SyntaxKind.ProtectedKeyword ||
anotherModifier.kind === SyntaxKind.PrivateKeyword)
) {
throw createError(
anotherModifier,
`Accessibility modifier already seen.`,
);
}
}
}
// `checkParameter` function in `typescript`
if (
node.kind === SyntaxKind.Parameter &&
// In `typescript` package, it's `ts.hasSyntacticModifier(node, ts.ModifierFlags.ParameterPropertyModifier)`
// https://github.com/typescript-eslint/typescript-eslint/pull/6615#discussion_r1136489935
(modifier.kind === SyntaxKind.PublicKeyword ||
modifier.kind === SyntaxKind.PrivateKeyword ||
modifier.kind === SyntaxKind.ProtectedKeyword ||
modifier.kind === SyntaxKind.ReadonlyKeyword ||
modifier.kind === SyntaxKind.OverrideKeyword)
) {
const func = getContainingFunction(node);
if (!(
func?.kind === SyntaxKind.Constructor && nodeIsPresent(func.body)
)) {
throw createError(
modifier,
'A parameter property is only allowed in a constructor implementation.',
);
}
const param = node as ts.ParameterDeclaration;
if (param.dotDotDotToken) {
throw createError(
modifier,
'A parameter property cannot be a rest parameter.',
);
}
if (
param.name.kind === SyntaxKind.ArrayBindingPattern ||
param.name.kind === SyntaxKind.ObjectBindingPattern
) {
throw createError(
modifier,
'A parameter property may not be declared using a binding pattern.',
);
}
}
checkObjectPropertyModifier(node, modifier);
}
// `ts.getModifiers()` can't access invalid modifiers on object properties
// Eg: `({declare a: 1})`
// See https://github.com/typescript-eslint/typescript-eslint/pull/11931#discussion_r2678961730
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- incorrect type
if (node.parent?.kind === SyntaxKind.ObjectLiteralExpression) {
// @ts-expect-error intentional to access deprecated `node.modifiers`
for (const modifier of (node.modifiers as ts.Modifier[] | undefined) ??
[]) {
if (ts.isDecorator(modifier) || modifiers.includes(modifier)) {
continue;
}
checkObjectPropertyModifier(node, modifier);
}
}
}
nodeIsMissing(node: ts.Node | undefined): boolean¶
Parameters:
nodets.Node | undefined
Returns: boolean
Code
nodeIsPresent(node: ts.Node | undefined): node is ts.Node¶
Parameters:
nodets.Node | undefined
Returns: node is ts.Node
Calls:
nodeIsMissing
Code
hasAbstractModifier(node: ts.Node): boolean¶
Parameters:
nodets.Node
Returns: boolean
Calls:
hasModifier (from ./node-utils)
Code
getThisParameter(signature: ts.SignatureDeclaration): ts.ParameterDeclaration | null¶
Parameters:
signaturets.SignatureDeclaration
Returns: ts.ParameterDeclaration | null
Calls:
ts.isJSDocSignatureparameterIsThisKeyword
Code
function getThisParameter(
signature: ts.SignatureDeclaration,
): ts.ParameterDeclaration | null {
if (signature.parameters.length && !ts.isJSDocSignature(signature)) {
const thisParameter = signature.parameters[0];
if (parameterIsThisKeyword(thisParameter)) {
return thisParameter;
}
}
return null;
}
parameterIsThisKeyword(parameter: ts.ParameterDeclaration): boolean¶
Parameters:
parameterts.ParameterDeclaration
Returns: boolean
Calls:
isThisIdentifier (from ./node-utils)
Code
getContainingFunction(node: ts.Node): ts.SignatureDeclaration | undefined¶
Parameters:
nodets.Node
Returns: ts.SignatureDeclaration | undefined
Calls:
ts.findAncestor
Code
nodeCanBeDecorated(node: TSNode): boolean¶
Parameters:
nodeTSNode
Returns: boolean
Calls:
ts.isClassDeclarationts.isClassLikehasAbstractModifierBooleangetThisParameter
Internal Comments:
// `ts.nodeCanBeDecorated` returns `false` if `useLegacyDecorators: true`
// `ts.nodeCanBeDecorated` uses this if `useLegacyDecorators: true`
// `ts.nodeCanBeDecorated` uses this if `useLegacyDecorators: false`
// parent is already a ClassLike subtype; defensive check
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
// In `ts.nodeCanBeDecorated`
// when `useLegacyDecorators: true` uses `ts.isClassDeclaration`
// when `useLegacyDecorators: true` uses `ts.isClassLike`
// `ts.nodeCanBeDecorated` returns `false` if `useLegacyDecorators: false` (x2)
Code
function nodeCanBeDecorated(node: TSNode): boolean {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
return true;
case SyntaxKind.ClassExpression:
// `ts.nodeCanBeDecorated` returns `false` if `useLegacyDecorators: true`
return true;
case SyntaxKind.PropertyDeclaration: {
const { parent } = node;
// `ts.nodeCanBeDecorated` uses this if `useLegacyDecorators: true`
if (ts.isClassDeclaration(parent)) {
return true;
}
// `ts.nodeCanBeDecorated` uses this if `useLegacyDecorators: false`
// parent is already a ClassLike subtype; defensive check
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (ts.isClassLike(parent) && !hasAbstractModifier(node)) {
return true;
}
return false;
}
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration: {
const { parent } = node;
// In `ts.nodeCanBeDecorated`
// when `useLegacyDecorators: true` uses `ts.isClassDeclaration`
// when `useLegacyDecorators: true` uses `ts.isClassLike`
return (
Boolean(node.body) &&
(ts.isClassDeclaration(parent) || ts.isClassLike(parent))
);
}
case SyntaxKind.Parameter: {
// `ts.nodeCanBeDecorated` returns `false` if `useLegacyDecorators: false`
const { parent } = node;
const grandparent = parent.parent;
return (
Boolean(parent) &&
'body' in parent &&
Boolean(parent.body) &&
(parent.kind === SyntaxKind.Constructor ||
parent.kind === SyntaxKind.MethodDeclaration ||
parent.kind === SyntaxKind.SetAccessor) &&
getThisParameter(parent) !== node &&
Boolean(grandparent) &&
grandparent.kind === SyntaxKind.ClassDeclaration
);
}
}
return false;
}
nodeHasIllegalDecorators(node: ts.Node): node is { illegalDecorators: ts.Node[] } & ts.Node¶
Parameters:
nodets.Node
Returns: node is { illegalDecorators: ts.Node[] } & ts.Node
Code
checkObjectPropertyModifier(node: ts.Node, modifier: ts.Modifier): void¶
Parameters:
nodets.Nodemodifierts.Modifier
Returns: void
Calls:
createError (from ./node-utils)ts.tokenToString
Internal Comments:
Code
function checkObjectPropertyModifier(node: ts.Node, modifier: ts.Modifier) {
// From `checkGrammarObjectLiteralExpression` function in `typescript`
if (
(modifier.kind !== SyntaxKind.AsyncKeyword ||
node.kind !== SyntaxKind.MethodDeclaration) &&
node.parent.kind === SyntaxKind.ObjectLiteralExpression
) {
throw createError(
modifier,
`'${ts.tokenToString(modifier.kind)}' modifier cannot be used here.`,
);
}
}
Generated by Syntax Scribe