JavaScript programs vary widely in functionality, complexity, and use, and analyses of these programs must accommodate such variations. Type-based analyses are typically the simplest such analyses, but due to the language's subtle idioms and many application-specific needs-such as ensuring general-purpose type correctness, security properties, or proper library usage-we have found that a single type system does not suffice for all purposes. However, these varied uses still share many reusable common elements.In this paper we present TeJaS, a framework for building type systems for JavaScript. TeJaS has been engineered modularly to encourage experimentation. Its initial type environment is reified, to admit easy modeling of the various execution contexts of JavaScript programs, and its type language and typing rules are extensible, to enable variations of the type system to be constructed easily.The paper presents the base TeJaS type system, which performs traditional type-checking for JavaScript. Because JavaScript demands complex types, we explain several design decisions to improve user ergonomics. We then describe TeJaS's modular structure, and illustrate it by reconstructing the essence of a very different type system for JavaScript. Systems built from TeJaS have been applied to several real-world, third-party JavaScript programs.There is a venerable line of research on retrofitting type systems onto previously "untyped" languages, with Morris's seminal dissertation [11] an early example. In practice, these languages are not actually free of types; rather, all type discrimination is performed by primitive operations at runtime. A common practice for retrofitting type systems has been to identify the resulting set of run-time errors and try to eliminate them statically. For instance, Smalltalk type systems [1, 22] focused on eliminating message-not-found errors, and Soft Scheme [25] addressed the wide range of Scheme dynamic errors. Identifying run-time errors and catching them statically can be viewed as the design principle for retrofitted type systems.Unfortunately, this principle is rather difficult to apply to JavaScript (JS), because there are so few actual run-time errors. 1 In this, JS inherits the forgiving mentality of the browser environment where it was born; instead of flagging errors and halting execution, it simply continues running. As a result, operations that in other languages might reasonably be expected to generate errors do not in JS: subtracting one string from another, accessing an array outside its bounds, reading and writing non-existent fields, calling functions with the wrong number of arguments, etc.Obviously, every one of these operations has a welldefined semantics in JS (usually, ironically, involving the value undefined). Therefore, it becomes a purpose-specific judgment call whether or not these should be considered static type errors. If the purpose is to enforce a strict, Javalike programming style, they should probably all be considered errors. If, in contrast, the goal is ...