Extract property name -> type from an interface body.
(body: str)
| 59 | |
| 60 | |
| 61 | def _parse_properties(body: str) -> dict: |
| 62 | """Extract property name -> type from an interface body.""" |
| 63 | props = {} |
| 64 | body = _strip_comments(body) |
| 65 | |
| 66 | tokens = body.strip() |
| 67 | if not tokens: |
| 68 | return props |
| 69 | |
| 70 | i = 0 |
| 71 | while i < len(tokens): |
| 72 | # Skip whitespace |
| 73 | while i < len(tokens) and tokens[i] in " \t\n\r": |
| 74 | i += 1 |
| 75 | if i >= len(tokens): |
| 76 | break |
| 77 | |
| 78 | # Try to match a property name (with optional ?) |
| 79 | prop_match = re.match(r"(readonly\s+)?(\w+)(\??)\s*:\s*", tokens[i:]) |
| 80 | if not prop_match: |
| 81 | # Skip to next semicolon or line |
| 82 | next_semi = tokens.find(";", i) |
| 83 | if next_semi == -1: |
| 84 | break |
| 85 | i = next_semi + 1 |
| 86 | continue |
| 87 | |
| 88 | prop_name = prop_match.group(2) |
| 89 | optional = prop_match.group(3) == "?" |
| 90 | i += prop_match.end() |
| 91 | |
| 92 | # Now extract the type until the matching semicolon, |
| 93 | # respecting nested braces, parens, angle brackets, and arrow functions |
| 94 | type_str, end = _extract_type(tokens, i) |
| 95 | if type_str is not None: |
| 96 | normalized = _normalize_type(type_str) |
| 97 | if optional: |
| 98 | props[prop_name] = f"{normalized} (optional)" |
| 99 | else: |
| 100 | props[prop_name] = normalized |
| 101 | i = end + 1 # skip the semicolon |
| 102 | else: |
| 103 | break |
| 104 | |
| 105 | return dict(sorted(props.items())) |
| 106 | |
| 107 | |
| 108 | def _extract_type(text: str, start: int): |
no test coverage detected