📄 no-base-to-string¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 13 |
| 📦 Imports | 10 |
| 📑 Type Aliases | 2 |
| 🎯 Enums | 1 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/no-base-to-string.ts
📤 Default Export¶
| Property | Value |
|---|---|
name |
'no-base-to-string' |
meta.type |
'suggestion' |
meta.docs.description |
'Require .toString() and .toLocaleString() to only be called on objects which provide useful information when str... |
meta.docs.recommended |
'recommended' |
meta.docs.requiresTypeChecking |
true |
meta.messages.baseArrayJoin |
"Using join() for {{name}} {{certainty}} use Object's default stringification format ('[object Object]') when strin... |
meta.messages.baseToString |
"'{{name}}' {{certainty}} use Object's default stringification format ('[object Object]') when stringified." |
meta.schema |
[ { type: 'object', additionalProperties: false, properties: { checkUnknown: { type: 'boolean', description: 'Whether... |
defaultOptions |
[ { checkUnknown: false, ignoredTypeNames: ['Error', 'RegExp', 'URL', 'URLSearchParams'], }, ] |
Entry point: create — documented under Functions.
📦 Imports¶
| Name | Source |
|---|---|
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
ASTUtils |
@typescript-eslint/utils |
createRule |
../util |
getConstrainedTypeAtLocation |
../util |
getParserServices |
../util |
getTypeName |
../util |
isSymbolFromDefaultLibrary |
../util |
matchesTypeOrBaseType |
../util |
nullThrows |
../util |
Functions¶
create(context: any, [option]: any): { 'AssignmentExpression[operator = "+="], BinaryExpression[…¶
Parameters:
contextany[option]any
Returns: { 'AssignmentExpression[operator = "+="], BinaryExpression[operator = "+"]'(node: TSESTree.AssignmentExpression | TSESTree.BinaryExpression): void; CallExpression(node: TSESTree.CallExpression): void; 'CallExpression > MemberExpression.callee > Identifier[name = "join"].property'(node: TSESTree.Expression): void; 'CallExpression > MemberExpression.callee > Identifier[name = /^(toLocaleString|toString)$/].property'(node: TSESTree.Expression): void; TemplateLiteral(node: TSESTree.TemplateLiteral): void; }
Calls:
getParserServices (from ../util)program.getTypeCheckercollectToStringCertaintyservices.getTypeAtLocationcontext.reportcontext.sourceCode.getTextcollectJoinCertaintytype.types.mapcollectSubTypeCertaintycertainties.everychecker.getTypeArgumentstypeArgs.mapcertainties.somenullThrows (from ../util)type.getNumberIndexTypetsutils.isUnionTypecollectUnionTypeCertaintytsutils.isIntersectionTypecollectIntersectionTypeCertaintychecker.isTupleTypecollectTupleCertaintychecker.isArrayTypecollectArrayCertaintyvisited.hastsutils.isTypeParametertype.getConstrainttsutils.isTypeFlagSettype.getSymbolsymbol?.getDeclarationscanHaveTypeParametersignoredTypeNames.includesmatchesTypeOrBaseType (from ../util)getTypeName (from ../util)type.isIntersectiontype.isUnionisToStringLikeFromObjectcontext.sourceCode.getScopeASTUtils.findVariablets.isMethodSignaturets.isComputedPropertyNamets.isPropertyAccessExpressionts.isIdentifierisSymbolFromDefaultLibrary (from ../util)checker.getSymbolAtLocationtype .getProperties() .someisSymbolToPrimitiveMethodchecker.getPropertyOfTypecandidate.getDeclarationsdeclarations.somets.isInterfaceDeclarationcheckExpressionisBuiltInStringCallgetConstrainedTypeAtLocation (from ../util)checkExpressionForArrayJoin
Internal Comments:
// don't report if this is a self referencing array or tuple type
// unconstrained generic means `unknown`
// the Boolean type definition missing toString()
// unknown
// e.g. any
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum (x6)
// An explicit [Symbol.toPrimitive] declaration is always user-defined
// Otherwise, we check for known methods used in type coercion. (x2)
// We'll try to find one that's not declared on Object itself. (x2)
// Failing that, we'll fall back to one that is. (x2)
// If any declaration is not from the Object interface, this is
// user-defined (e.g. overloaded toString on a class or module).
// see https://github.com/typescript-eslint/typescript-eslint/issues/8585
// see https://github.com/typescript-eslint/typescript-eslint/issues/11945
Code
create(context, [option]) {
const services = getParserServices(context);
const { program } = services;
const checker = program.getTypeChecker();
const ignoredTypeNames = option.ignoredTypeNames ?? [];
function checkExpression(node: TSESTree.Expression, type?: ts.Type): void {
if (node.type === AST_NODE_TYPES.Literal) {
return;
}
const certainty = collectToStringCertainty(
type ?? services.getTypeAtLocation(node),
new Set(),
);
if (certainty === Usefulness.Always) {
return;
}
context.report({
node,
messageId: 'baseToString',
data: {
name: context.sourceCode.getText(node),
certainty,
},
});
}
function checkExpressionForArrayJoin(
node: TSESTree.Node,
type: ts.Type,
): void {
const certainty = collectJoinCertainty(type, new Set());
if (certainty === Usefulness.Always) {
return;
}
context.report({
node,
messageId: 'baseArrayJoin',
data: {
name: context.sourceCode.getText(node),
certainty,
},
});
}
function collectUnionTypeCertainty(
type: ts.UnionType,
collectSubTypeCertainty: (type: ts.Type) => Usefulness,
): Usefulness {
const certainties = type.types.map(t => collectSubTypeCertainty(t));
if (certainties.every(certainty => certainty === Usefulness.Never)) {
return Usefulness.Never;
}
if (certainties.every(certainty => certainty === Usefulness.Always)) {
return Usefulness.Always;
}
return Usefulness.Sometimes;
}
function collectIntersectionTypeCertainty(
type: ts.IntersectionType,
collectSubTypeCertainty: (type: ts.Type) => Usefulness,
): Usefulness {
for (const subType of type.types) {
const subtypeUsefulness = collectSubTypeCertainty(subType);
if (subtypeUsefulness === Usefulness.Always) {
return Usefulness.Always;
}
}
return Usefulness.Never;
}
function collectTupleCertainty(
type: ts.TypeReference,
visited: Set<ts.Type>,
): Usefulness {
const typeArgs = checker.getTypeArguments(type);
const certainties = typeArgs.map(t =>
collectToStringCertainty(t, visited),
);
if (certainties.some(certainty => certainty === Usefulness.Never)) {
return Usefulness.Never;
}
if (certainties.some(certainty => certainty === Usefulness.Sometimes)) {
return Usefulness.Sometimes;
}
return Usefulness.Always;
}
function collectArrayCertainty(
type: ts.Type,
visited: Set<ts.Type>,
): Usefulness {
const elemType = nullThrows(
type.getNumberIndexType(),
'array should have number index type',
);
return collectToStringCertainty(elemType, visited);
}
function collectJoinCertainty(
type: ts.Type,
visited: Set<ts.Type>,
): Usefulness {
if (tsutils.isUnionType(type)) {
return collectUnionTypeCertainty(type, t =>
collectJoinCertainty(t, visited),
);
}
if (tsutils.isIntersectionType(type)) {
return collectIntersectionTypeCertainty(type, t =>
collectJoinCertainty(t, visited),
);
}
if (checker.isTupleType(type)) {
return collectTupleCertainty(type, visited);
}
if (checker.isArrayType(type)) {
return collectArrayCertainty(type, visited);
}
return Usefulness.Always;
}
function collectToStringCertainty(
type: ts.Type,
visited: Set<ts.Type>,
): Usefulness {
if (visited.has(type)) {
// don't report if this is a self referencing array or tuple type
return Usefulness.Always;
}
if (tsutils.isTypeParameter(type)) {
const constraint = type.getConstraint();
if (constraint) {
return collectToStringCertainty(constraint, visited);
}
// unconstrained generic means `unknown`
return option.checkUnknown ? Usefulness.Sometimes : Usefulness.Always;
}
// the Boolean type definition missing toString()
if (
tsutils.isTypeFlagSet(type, ts.TypeFlags.Boolean) ||
tsutils.isTypeFlagSet(type, ts.TypeFlags.BooleanLiteral)
) {
return Usefulness.Always;
}
const symbol = type.aliasSymbol ?? type.getSymbol();
const decl = symbol?.getDeclarations()?.[0];
if (
decl &&
canHaveTypeParameters(decl) &&
decl.typeParameters &&
ignoredTypeNames.includes(symbol.name)
) {
return Usefulness.Always;
}
if (
matchesTypeOrBaseType(
services,
type => ignoredTypeNames.includes(getTypeName(checker, type)),
type,
)
) {
return Usefulness.Always;
}
if (type.isIntersection()) {
return collectIntersectionTypeCertainty(type, t =>
collectToStringCertainty(t, visited),
);
}
if (type.isUnion()) {
return collectUnionTypeCertainty(type, t =>
collectToStringCertainty(t, visited),
);
}
if (checker.isTupleType(type)) {
return collectTupleCertainty(type, new Set([...visited, type]));
}
if (checker.isArrayType(type)) {
return collectArrayCertainty(type, new Set([...visited, type]));
}
switch (isToStringLikeFromObject(type)) {
case undefined:
// unknown
if (option.checkUnknown && type.flags === ts.TypeFlags.Unknown) {
return Usefulness.Sometimes;
}
// e.g. any
return Usefulness.Always;
case true:
return Usefulness.Never;
case false:
return Usefulness.Always;
}
}
function isBuiltInStringCall(node: TSESTree.CallExpression): boolean {
if (
node.callee.type === AST_NODE_TYPES.Identifier &&
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum
node.callee.name === 'String' &&
node.arguments[0]
) {
const scope = context.sourceCode.getScope(node);
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum
const variable = ASTUtils.findVariable(scope, 'String');
return !variable?.defs.length;
}
return false;
}
function isSymbolToPrimitiveMethod(node: ts.Declaration) {
return (
ts.isMethodSignature(node) &&
ts.isComputedPropertyName(node.name) &&
ts.isPropertyAccessExpression(node.name.expression) &&
ts.isIdentifier(node.name.expression.expression) &&
node.name.expression.expression.text === 'Symbol' &&
ts.isIdentifier(node.name.expression.name) &&
node.name.expression.name.text === 'toPrimitive' &&
isSymbolFromDefaultLibrary(
program,
checker.getSymbolAtLocation(node.name.expression.expression),
)
);
}
function isToStringLikeFromObject(type: ts.Type) {
// An explicit [Symbol.toPrimitive] declaration is always user-defined
if (
type
.getProperties()
.some(
property =>
property.valueDeclaration &&
isSymbolToPrimitiveMethod(property.valueDeclaration),
)
) {
return false;
}
// Otherwise, we check for known methods used in type coercion.
// We'll try to find one that's not declared on Object itself.
// Failing that, we'll fall back to one that is.
let foundFallbackOnObject = false;
for (const propertyName of ['toLocaleString', 'toString', 'valueOf']) {
const candidate = checker.getPropertyOfType(type, propertyName);
if (!candidate) {
continue;
}
const declarations = candidate.getDeclarations();
if (!declarations?.length) {
continue;
}
// If any declaration is not from the Object interface, this is
// user-defined (e.g. overloaded toString on a class or module).
// see https://github.com/typescript-eslint/typescript-eslint/issues/8585
// see https://github.com/typescript-eslint/typescript-eslint/issues/11945
if (
declarations.some(
declaration =>
!(
ts.isInterfaceDeclaration(declaration.parent) &&
declaration.parent.name.text === 'Object'
),
)
) {
return false;
}
foundFallbackOnObject = true;
}
return foundFallbackOnObject ? true : undefined;
}
return {
'AssignmentExpression[operator = "+="], BinaryExpression[operator = "+"]'(
node: TSESTree.AssignmentExpression | TSESTree.BinaryExpression,
): void {
const leftType = services.getTypeAtLocation(node.left);
const rightType = services.getTypeAtLocation(node.right);
if (getTypeName(checker, leftType) === 'string') {
checkExpression(node.right, rightType);
} else if (
node.left.type !== AST_NODE_TYPES.PrivateIdentifier &&
getTypeName(checker, rightType) === 'string'
) {
checkExpression(node.left, leftType);
}
},
CallExpression(node: TSESTree.CallExpression): void {
if (
isBuiltInStringCall(node) &&
node.arguments[0].type !== AST_NODE_TYPES.SpreadElement
) {
checkExpression(node.arguments[0]);
}
},
'CallExpression > MemberExpression.callee > Identifier[name = "join"].property'(
node: TSESTree.Expression,
): void {
const memberExpr = node.parent as TSESTree.MemberExpression;
const type = getConstrainedTypeAtLocation(services, memberExpr.object);
checkExpressionForArrayJoin(memberExpr.object, type);
},
'CallExpression > MemberExpression.callee > Identifier[name = /^(toLocaleString|toString)$/].property'(
node: TSESTree.Expression,
): void {
const memberExpr = node.parent as TSESTree.MemberExpression;
checkExpression(memberExpr.object);
},
TemplateLiteral(node: TSESTree.TemplateLiteral): void {
if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) {
return;
}
for (const expression of node.expressions) {
checkExpression(expression);
}
},
};
}
canHaveTypeParameters(declaration: ts.Declaration): any¶
Parameters:
declarationts.Declaration
Returns: any
Calls:
ts.isTypeAliasDeclarationts.isInterfaceDeclarationts.isClassDeclaration
Code
Internal helpers¶
Declared inside another function in this file.
checkExpression(node: TSESTree.Expression, type: ts.Type): void¶
Parameters:
nodeTSESTree.Expressiontypets.Type
Returns: void
Calls:
collectToStringCertaintyservices.getTypeAtLocationcontext.reportcontext.sourceCode.getText
Code
function checkExpression(node: TSESTree.Expression, type?: ts.Type): void {
if (node.type === AST_NODE_TYPES.Literal) {
return;
}
const certainty = collectToStringCertainty(
type ?? services.getTypeAtLocation(node),
new Set(),
);
if (certainty === Usefulness.Always) {
return;
}
context.report({
node,
messageId: 'baseToString',
data: {
name: context.sourceCode.getText(node),
certainty,
},
});
}
checkExpressionForArrayJoin(node: TSESTree.Node, type: ts.Type): void¶
Parameters:
nodeTSESTree.Nodetypets.Type
Returns: void
Calls:
collectJoinCertaintycontext.reportcontext.sourceCode.getText
Code
function checkExpressionForArrayJoin(
node: TSESTree.Node,
type: ts.Type,
): void {
const certainty = collectJoinCertainty(type, new Set());
if (certainty === Usefulness.Always) {
return;
}
context.report({
node,
messageId: 'baseArrayJoin',
data: {
name: context.sourceCode.getText(node),
certainty,
},
});
}
collectUnionTypeCertainty(type: ts.UnionType, collectSubTypeCertainty: (type: ts.Type) => Usefulness): Usefulness¶
Parameters:
typets.UnionTypecollectSubTypeCertainty(type: ts.Type) => Usefulness
Returns: Usefulness
Calls:
type.types.mapcollectSubTypeCertaintycertainties.every
Code
function collectUnionTypeCertainty(
type: ts.UnionType,
collectSubTypeCertainty: (type: ts.Type) => Usefulness,
): Usefulness {
const certainties = type.types.map(t => collectSubTypeCertainty(t));
if (certainties.every(certainty => certainty === Usefulness.Never)) {
return Usefulness.Never;
}
if (certainties.every(certainty => certainty === Usefulness.Always)) {
return Usefulness.Always;
}
return Usefulness.Sometimes;
}
collectIntersectionTypeCertainty(type: ts.IntersectionType, collectSubTypeCertainty: (type: ts.Type) => Usefulness): Usefulness¶
Parameters:
typets.IntersectionTypecollectSubTypeCertainty(type: ts.Type) => Usefulness
Returns: Usefulness
Calls:
collectSubTypeCertainty
Code
function collectIntersectionTypeCertainty(
type: ts.IntersectionType,
collectSubTypeCertainty: (type: ts.Type) => Usefulness,
): Usefulness {
for (const subType of type.types) {
const subtypeUsefulness = collectSubTypeCertainty(subType);
if (subtypeUsefulness === Usefulness.Always) {
return Usefulness.Always;
}
}
return Usefulness.Never;
}
collectTupleCertainty(type: ts.TypeReference, visited: Set<ts.Type>): Usefulness¶
Parameters:
typets.TypeReferencevisitedSet<ts.Type>
Returns: Usefulness
Calls:
checker.getTypeArgumentstypeArgs.mapcollectToStringCertaintycertainties.some
Code
function collectTupleCertainty(
type: ts.TypeReference,
visited: Set<ts.Type>,
): Usefulness {
const typeArgs = checker.getTypeArguments(type);
const certainties = typeArgs.map(t =>
collectToStringCertainty(t, visited),
);
if (certainties.some(certainty => certainty === Usefulness.Never)) {
return Usefulness.Never;
}
if (certainties.some(certainty => certainty === Usefulness.Sometimes)) {
return Usefulness.Sometimes;
}
return Usefulness.Always;
}
collectArrayCertainty(type: ts.Type, visited: Set<ts.Type>): Usefulness¶
Parameters:
typets.TypevisitedSet<ts.Type>
Returns: Usefulness
Calls:
nullThrows (from ../util)type.getNumberIndexTypecollectToStringCertainty
Code
collectJoinCertainty(type: ts.Type, visited: Set<ts.Type>): Usefulness¶
Parameters:
typets.TypevisitedSet<ts.Type>
Returns: Usefulness
Calls:
tsutils.isUnionTypecollectUnionTypeCertaintycollectJoinCertaintytsutils.isIntersectionTypecollectIntersectionTypeCertaintychecker.isTupleTypecollectTupleCertaintychecker.isArrayTypecollectArrayCertainty
Code
function collectJoinCertainty(
type: ts.Type,
visited: Set<ts.Type>,
): Usefulness {
if (tsutils.isUnionType(type)) {
return collectUnionTypeCertainty(type, t =>
collectJoinCertainty(t, visited),
);
}
if (tsutils.isIntersectionType(type)) {
return collectIntersectionTypeCertainty(type, t =>
collectJoinCertainty(t, visited),
);
}
if (checker.isTupleType(type)) {
return collectTupleCertainty(type, visited);
}
if (checker.isArrayType(type)) {
return collectArrayCertainty(type, visited);
}
return Usefulness.Always;
}
collectToStringCertainty(type: ts.Type, visited: Set<ts.Type>): Usefulness¶
Parameters:
typets.TypevisitedSet<ts.Type>
Returns: Usefulness
Calls:
visited.hastsutils.isTypeParametertype.getConstraintcollectToStringCertaintytsutils.isTypeFlagSettype.getSymbolsymbol?.getDeclarationscanHaveTypeParametersignoredTypeNames.includesmatchesTypeOrBaseType (from ../util)getTypeName (from ../util)type.isIntersectioncollectIntersectionTypeCertaintytype.isUnioncollectUnionTypeCertaintychecker.isTupleTypecollectTupleCertaintychecker.isArrayTypecollectArrayCertaintyisToStringLikeFromObject
Internal Comments:
// don't report if this is a self referencing array or tuple type
// unconstrained generic means `unknown`
// the Boolean type definition missing toString()
// unknown
// e.g. any
Code
function collectToStringCertainty(
type: ts.Type,
visited: Set<ts.Type>,
): Usefulness {
if (visited.has(type)) {
// don't report if this is a self referencing array or tuple type
return Usefulness.Always;
}
if (tsutils.isTypeParameter(type)) {
const constraint = type.getConstraint();
if (constraint) {
return collectToStringCertainty(constraint, visited);
}
// unconstrained generic means `unknown`
return option.checkUnknown ? Usefulness.Sometimes : Usefulness.Always;
}
// the Boolean type definition missing toString()
if (
tsutils.isTypeFlagSet(type, ts.TypeFlags.Boolean) ||
tsutils.isTypeFlagSet(type, ts.TypeFlags.BooleanLiteral)
) {
return Usefulness.Always;
}
const symbol = type.aliasSymbol ?? type.getSymbol();
const decl = symbol?.getDeclarations()?.[0];
if (
decl &&
canHaveTypeParameters(decl) &&
decl.typeParameters &&
ignoredTypeNames.includes(symbol.name)
) {
return Usefulness.Always;
}
if (
matchesTypeOrBaseType(
services,
type => ignoredTypeNames.includes(getTypeName(checker, type)),
type,
)
) {
return Usefulness.Always;
}
if (type.isIntersection()) {
return collectIntersectionTypeCertainty(type, t =>
collectToStringCertainty(t, visited),
);
}
if (type.isUnion()) {
return collectUnionTypeCertainty(type, t =>
collectToStringCertainty(t, visited),
);
}
if (checker.isTupleType(type)) {
return collectTupleCertainty(type, new Set([...visited, type]));
}
if (checker.isArrayType(type)) {
return collectArrayCertainty(type, new Set([...visited, type]));
}
switch (isToStringLikeFromObject(type)) {
case undefined:
// unknown
if (option.checkUnknown && type.flags === ts.TypeFlags.Unknown) {
return Usefulness.Sometimes;
}
// e.g. any
return Usefulness.Always;
case true:
return Usefulness.Never;
case false:
return Usefulness.Always;
}
}
isBuiltInStringCall(node: TSESTree.CallExpression): boolean¶
Parameters:
nodeTSESTree.CallExpression
Returns: boolean
Calls:
context.sourceCode.getScopeASTUtils.findVariable
Internal Comments:
Code
function isBuiltInStringCall(node: TSESTree.CallExpression): boolean {
if (
node.callee.type === AST_NODE_TYPES.Identifier &&
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum
node.callee.name === 'String' &&
node.arguments[0]
) {
const scope = context.sourceCode.getScope(node);
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum
const variable = ASTUtils.findVariable(scope, 'String');
return !variable?.defs.length;
}
return false;
}
isSymbolToPrimitiveMethod(node: ts.Declaration): any¶
Parameters:
nodets.Declaration
Returns: any
Calls:
ts.isMethodSignaturets.isComputedPropertyNamets.isPropertyAccessExpressionts.isIdentifierisSymbolFromDefaultLibrary (from ../util)checker.getSymbolAtLocation
Code
function isSymbolToPrimitiveMethod(node: ts.Declaration) {
return (
ts.isMethodSignature(node) &&
ts.isComputedPropertyName(node.name) &&
ts.isPropertyAccessExpression(node.name.expression) &&
ts.isIdentifier(node.name.expression.expression) &&
node.name.expression.expression.text === 'Symbol' &&
ts.isIdentifier(node.name.expression.name) &&
node.name.expression.name.text === 'toPrimitive' &&
isSymbolFromDefaultLibrary(
program,
checker.getSymbolAtLocation(node.name.expression.expression),
)
);
}
isToStringLikeFromObject(type: ts.Type): boolean¶
Parameters:
typets.Type
Returns: boolean
Calls:
type .getProperties() .someisSymbolToPrimitiveMethodchecker.getPropertyOfTypecandidate.getDeclarationsdeclarations.somets.isInterfaceDeclaration
Internal Comments:
// An explicit [Symbol.toPrimitive] declaration is always user-defined
// Otherwise, we check for known methods used in type coercion. (x2)
// We'll try to find one that's not declared on Object itself. (x2)
// Failing that, we'll fall back to one that is. (x2)
// If any declaration is not from the Object interface, this is
// user-defined (e.g. overloaded toString on a class or module).
// see https://github.com/typescript-eslint/typescript-eslint/issues/8585
// see https://github.com/typescript-eslint/typescript-eslint/issues/11945
Code
function isToStringLikeFromObject(type: ts.Type) {
// An explicit [Symbol.toPrimitive] declaration is always user-defined
if (
type
.getProperties()
.some(
property =>
property.valueDeclaration &&
isSymbolToPrimitiveMethod(property.valueDeclaration),
)
) {
return false;
}
// Otherwise, we check for known methods used in type coercion.
// We'll try to find one that's not declared on Object itself.
// Failing that, we'll fall back to one that is.
let foundFallbackOnObject = false;
for (const propertyName of ['toLocaleString', 'toString', 'valueOf']) {
const candidate = checker.getPropertyOfType(type, propertyName);
if (!candidate) {
continue;
}
const declarations = candidate.getDeclarations();
if (!declarations?.length) {
continue;
}
// If any declaration is not from the Object interface, this is
// user-defined (e.g. overloaded toString on a class or module).
// see https://github.com/typescript-eslint/typescript-eslint/issues/8585
// see https://github.com/typescript-eslint/typescript-eslint/issues/11945
if (
declarations.some(
declaration =>
!(
ts.isInterfaceDeclaration(declaration.parent) &&
declaration.parent.name.text === 'Object'
),
)
) {
return false;
}
foundFallbackOnObject = true;
}
return foundFallbackOnObject ? true : undefined;
}
Type Aliases¶
Options¶
MessageIds¶
Enums¶
enum Usefulness¶
Members¶
| Name | Value | Description |
|---|---|---|
Always |
always |
not shown |
Never |
will |
not shown |
Sometimes |
may |
not shown |
Generated by Syntax Scribe