Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
"Program:exit"(program) {
const components = ctx.getAllComponents(program);
for (const [, component] of components) {
if (component.id == null) continue;
if (component.name == null) continue;
const [props] = component.node.params;
if (props == null) {
continue;
}
// Skip if the component is anonymous to reduce false positives
if (component.id == null || component.name == null) continue;
// Skip if no props
if (props == null) continue;

const propsType = getConstrainedTypeAtLocation(services, props);
if (isTypeReadonly(services.program, propsType)) continue;
// Handle edge case where isTypeReadonly cant detect some readonly or immutable types
Expand All @@ -76,18 +76,16 @@ function isTypeReadonlyLoose(services: ParserServicesWithTypeInformation, type:
// TODO: A comprehensive test is required to verify that it works as expected
// @see https://github.com/Rel1cx/eslint-react/issues/1326
function isClassOrInterfaceReadonlyLoose(checker: ts.TypeChecker, type: ts.Type) {
const props = type.getProperties();
const baseTypes = type.getBaseTypes() ?? [];
const properties = type.getProperties();
if (properties.length === 0) {
if (props.length === 0) {
return true;
}
if (baseTypes.length === 0) {
return properties.every((property) => isPropertyReadonlyInType(type, property.getEscapedName(), checker));
}
for (const property of properties) {
const propertyName = property.getEscapedName();
if (isPropertyReadonlyInType(type, propertyName, checker)) continue;
else return baseTypes.every((heritageType) => isPropertyReadonlyInType(heritageType, propertyName, checker));
return props.every((prop) => isPropertyReadonlyInType(type, prop.getEscapedName(), checker));
}
return true;
return props.every((prop) => {
if (isPropertyReadonlyInType(type, prop.getEscapedName(), checker)) return true;
return baseTypes.every((type) => isPropertyReadonlyInType(type, prop.getEscapedName(), checker));
});
}