Skip to content

Commit b1f611d

Browse files
committed
[compiler] Validate type configs for hooks/non-hooks
Alternative to #30868. The goal is to ensure that the types coming out of moduleTypeProvider are valid wrt to hook typing. If something is named like a hook, then it must be typed as a hook (or don't type it). ghstack-source-id: ea413d6 Pull Request resolved: #30888
1 parent 4c58fce commit b1f611d

File tree

1 file changed

+28
-6
lines changed

1 file changed

+28
-6
lines changed

compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
Type,
3434
ValidatedIdentifier,
3535
ValueKind,
36+
getHookKindForType,
3637
makeBlockId,
3738
makeIdentifierId,
3839
makeIdentifierName,
@@ -787,13 +788,23 @@ export class Environment {
787788
(isHookName(binding.imported) ? this.#getCustomHookType() : null)
788789
);
789790
} else {
791+
const expectHook =
792+
isHookName(binding.imported) || isHookName(binding.name);
790793
const moduleType = this.#resolveModuleType(binding.module, loc);
791794
if (moduleType !== null) {
792795
const importedType = this.getPropertyType(
793796
moduleType,
794797
binding.imported,
795798
);
796799
if (importedType != null) {
800+
const isHook = getHookKindForType(this, importedType) != null;
801+
if (expectHook !== isHook) {
802+
CompilerError.throwInvalidConfig({
803+
reason: `Invalid type configuration for module`,
804+
description: `Expected type for '${binding.module}.${binding.imported}' to ${expectHook ? 'be a hook' : 'not be a hook'} based on its name`,
805+
loc,
806+
});
807+
}
797808
return importedType;
798809
}
799810
}
@@ -806,9 +817,7 @@ export class Environment {
806817
* `import {useHook as foo} ...`
807818
* `import {foo as useHook} ...`
808819
*/
809-
return isHookName(binding.imported) || isHookName(binding.name)
810-
? this.#getCustomHookType()
811-
: null;
820+
return expectHook ? this.#getCustomHookType() : null;
812821
}
813822
}
814823
case 'ImportDefault':
@@ -820,18 +829,31 @@ export class Environment {
820829
(isHookName(binding.name) ? this.#getCustomHookType() : null)
821830
);
822831
} else {
832+
const expectHook = isHookName(binding.name);
823833
const moduleType = this.#resolveModuleType(binding.module, loc);
824834
if (moduleType !== null) {
835+
let importedType: Type | null = null;
825836
if (binding.kind === 'ImportDefault') {
826837
const defaultType = this.getPropertyType(moduleType, 'default');
827838
if (defaultType !== null) {
828-
return defaultType;
839+
importedType = defaultType;
829840
}
830841
} else {
831-
return moduleType;
842+
importedType = moduleType;
843+
}
844+
if (importedType !== null) {
845+
const isHook = getHookKindForType(this, importedType) != null;
846+
if (expectHook !== isHook) {
847+
CompilerError.throwInvalidConfig({
848+
reason: `Invalid type configuration for module`,
849+
description: `Expected type for '${binding.module}' (as '${binding.name}') to ${expectHook ? 'be a hook' : 'not be a hook'} based on its name`,
850+
loc,
851+
});
852+
}
853+
return importedType;
832854
}
833855
}
834-
return isHookName(binding.name) ? this.#getCustomHookType() : null;
856+
return expectHook ? this.#getCustomHookType() : null;
835857
}
836858
}
837859
}

0 commit comments

Comments
 (0)