inferer-reference.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = _default;
  6. var t = require("@babel/types");
  7. function _default(node) {
  8. if (!this.isReferenced()) return;
  9. const binding = this.scope.getBinding(node.name);
  10. if (binding) {
  11. if (binding.identifier.typeAnnotation) {
  12. return binding.identifier.typeAnnotation;
  13. } else {
  14. return getTypeAnnotationBindingConstantViolations(binding, this, node.name);
  15. }
  16. }
  17. if (node.name === "undefined") {
  18. return t.voidTypeAnnotation();
  19. } else if (node.name === "NaN" || node.name === "Infinity") {
  20. return t.numberTypeAnnotation();
  21. } else if (node.name === "arguments") {}
  22. }
  23. function getTypeAnnotationBindingConstantViolations(binding, path, name) {
  24. const types = [];
  25. const functionConstantViolations = [];
  26. let constantViolations = getConstantViolationsBefore(binding, path, functionConstantViolations);
  27. const testType = getConditionalAnnotation(binding, path, name);
  28. if (testType) {
  29. const testConstantViolations = getConstantViolationsBefore(binding, testType.ifStatement);
  30. constantViolations = constantViolations.filter(path => testConstantViolations.indexOf(path) < 0);
  31. types.push(testType.typeAnnotation);
  32. }
  33. if (constantViolations.length) {
  34. constantViolations = constantViolations.concat(functionConstantViolations);
  35. for (const violation of constantViolations) {
  36. types.push(violation.getTypeAnnotation());
  37. }
  38. }
  39. if (!types.length) {
  40. return;
  41. }
  42. if (t.isTSTypeAnnotation(types[0]) && t.createTSUnionType) {
  43. return t.createTSUnionType(types);
  44. }
  45. if (t.createFlowUnionType) {
  46. return t.createFlowUnionType(types);
  47. }
  48. return t.createUnionTypeAnnotation(types);
  49. }
  50. function getConstantViolationsBefore(binding, path, functions) {
  51. const violations = binding.constantViolations.slice();
  52. violations.unshift(binding.path);
  53. return violations.filter(violation => {
  54. violation = violation.resolve();
  55. const status = violation._guessExecutionStatusRelativeTo(path);
  56. if (functions && status === "unknown") functions.push(violation);
  57. return status === "before";
  58. });
  59. }
  60. function inferAnnotationFromBinaryExpression(name, path) {
  61. const operator = path.node.operator;
  62. const right = path.get("right").resolve();
  63. const left = path.get("left").resolve();
  64. let target;
  65. if (left.isIdentifier({
  66. name
  67. })) {
  68. target = right;
  69. } else if (right.isIdentifier({
  70. name
  71. })) {
  72. target = left;
  73. }
  74. if (target) {
  75. if (operator === "===") {
  76. return target.getTypeAnnotation();
  77. }
  78. if (t.BOOLEAN_NUMBER_BINARY_OPERATORS.indexOf(operator) >= 0) {
  79. return t.numberTypeAnnotation();
  80. }
  81. return;
  82. }
  83. if (operator !== "===" && operator !== "==") return;
  84. let typeofPath;
  85. let typePath;
  86. if (left.isUnaryExpression({
  87. operator: "typeof"
  88. })) {
  89. typeofPath = left;
  90. typePath = right;
  91. } else if (right.isUnaryExpression({
  92. operator: "typeof"
  93. })) {
  94. typeofPath = right;
  95. typePath = left;
  96. }
  97. if (!typeofPath) return;
  98. if (!typeofPath.get("argument").isIdentifier({
  99. name
  100. })) return;
  101. typePath = typePath.resolve();
  102. if (!typePath.isLiteral()) return;
  103. const typeValue = typePath.node.value;
  104. if (typeof typeValue !== "string") return;
  105. return t.createTypeAnnotationBasedOnTypeof(typeValue);
  106. }
  107. function getParentConditionalPath(binding, path, name) {
  108. let parentPath;
  109. while (parentPath = path.parentPath) {
  110. if (parentPath.isIfStatement() || parentPath.isConditionalExpression()) {
  111. if (path.key === "test") {
  112. return;
  113. }
  114. return parentPath;
  115. }
  116. if (parentPath.isFunction()) {
  117. if (parentPath.parentPath.scope.getBinding(name) !== binding) return;
  118. }
  119. path = parentPath;
  120. }
  121. }
  122. function getConditionalAnnotation(binding, path, name) {
  123. const ifStatement = getParentConditionalPath(binding, path, name);
  124. if (!ifStatement) return;
  125. const test = ifStatement.get("test");
  126. const paths = [test];
  127. const types = [];
  128. for (let i = 0; i < paths.length; i++) {
  129. const path = paths[i];
  130. if (path.isLogicalExpression()) {
  131. if (path.node.operator === "&&") {
  132. paths.push(path.get("left"));
  133. paths.push(path.get("right"));
  134. }
  135. } else if (path.isBinaryExpression()) {
  136. const type = inferAnnotationFromBinaryExpression(name, path);
  137. if (type) types.push(type);
  138. }
  139. }
  140. if (types.length) {
  141. if (t.isTSTypeAnnotation(types[0]) && t.createTSUnionType) {
  142. return {
  143. typeAnnotation: t.createTSUnionType(types),
  144. ifStatement
  145. };
  146. }
  147. if (t.createFlowUnionType) {
  148. return {
  149. typeAnnotation: t.createFlowUnionType(types),
  150. ifStatement
  151. };
  152. }
  153. return {
  154. typeAnnotation: t.createUnionTypeAnnotation(types),
  155. ifStatement
  156. };
  157. }
  158. return getConditionalAnnotation(ifStatement, name);
  159. }