ADR-0011: camelCase API Convention
ADR-0011: camelCase API Convention
Status
Accepted
Context
Python uses snake_case for function names (e.g., lru_cache, zip_longest, ascii_lowercase),
while JavaScript/TypeScript conventions use camelCase (e.g., lruCache, zipLongest,
asciiLowercase).
When building a Python standard library for TypeScript developers, we face a design choice:
- Keep Python naming — familiar for Python developers, but feels foreign in TypeScript
- Use TypeScript naming — idiomatic for TypeScript, but requires learning new names
Decision
We adopt camelCase naming throughout pythonlib, providing a truly TypeScript-native experience:
Naming Transformations
| Python (snake_case) | TypeScript (camelCase) |
|---|---|
lru_cache | lruCache |
zip_longest | zipLongest |
cache_info | cacheInfo |
total_seconds | totalSeconds |
ascii_lowercase | asciiLowercase |
startswith | startsWith |
groupdict | groupDict |
Implementation
-
Central Name Mapping: A
name-mappings.tsfile provides the canonical Python→JS name mapping:export const PYTHON_TO_JS_NAMES: Record<string, string> = {lru_cache: "lruCache",zip_longest: "zipLongest"// ...}export function toJsName(pythonName: string): string {return PYTHON_TO_JS_NAMES[pythonName] ?? pythonName} -
Transformer Integration: The transpiler automatically converts Python snake_case calls to JavaScript camelCase:
# Python inputfrom functools import lru_cache@lru_cachedef fib(n): return n// TypeScript outputimport { lruCache } from "pythonlib/functools"const fib = lruCache((n) => n)
Complete Mapping by Module
functools:
lru_cache→lruCache,cache_info→cacheInfo,cache_clear→cacheClearpartial_method→partialMethod,single_dispatch→singleDispatchattr_getter→attrGetter,item_getter→itemGetter,method_caller→methodCallercmp_to_key→cmpToKey,total_ordering→totalOrdering
itertools:
zip_longest→zipLongest,takewhile→takeWhile,dropwhile→dropWhilefilterfalse→filterFalse,combinations_with_replacement→combinationsWithReplacement
collections:
appendleft→appendLeft,popleft→popLeft,extendleft→extendLeft
datetime:
total_seconds→totalSeconds,isoformat→isoFormatfromisoformat→fromIsoFormat,fromtimestamp→fromTimestampisoweekday→isoWeekday,isocalendar→isoCalendar
random:
randint→randInt,randrange→randRangebetavariate→betaVariate,expovariate→expoVariate(and other *variate functions)
string:
ascii_lowercase→asciiLowercase,ascii_uppercase→asciiUppercasestartswith→startsWith,endswith→endsWithsafe_substitute→safeSubstitute,get_identifiers→getIdentifiers
os:
getcwd→getCwd,splitext→splitExt,normpath→normPathisabs→isAbs,relpath→relPath,commonpath→commonPath
re:
fullmatch→fullMatch,findall→findAll,finditer→findItergroupdict→groupDict
json:
sort_keys→sortKeys,ensure_ascii→ensureAscii
dict/set:
setdefault→setDefault,popitem→popItem,fromkeys→fromKeysissubset→isSubset,issuperset→isSuperset,isdisjoint→isDisjoint
core:
floordiv→floorDiv,divmod→divMod
Consequences
Positive
- Idiomatic TypeScript: Code feels native to TypeScript developers
- IDE Support: Better autocomplete with standard JS naming conventions
- Consistency: All pythonlib APIs follow the same camelCase pattern
- Marketing: “Python’s powerful APIs, TypeScript’s familiar style”
Negative
- Learning Curve for Python Developers: Must learn new names initially
- Breaking Change: Existing code using snake_case must be updated
Example
Python:
from functools import lru_cachefrom itertools import zip_longest
@lru_cache(maxsize=128)def process(items): return list(zip_longest(items, fillvalue=0))TypeScript:
import { lruCache } from "pythonlib/functools"import { zipLongest } from "pythonlib/itertools"
const process = lruCache( (items) => { return zipLongest(items, { fillvalue: 0 }) }, { maxsize: 128 })