Skip to content

AI Translated Document

  • This document was translated using an AI translation tool.
  • Due to the nature of AI translation, some sentences or terms may be interpreted differently from the original intent.
  • There may be mistranslations or inaccuracies, so please refer to the original text or consider additional review if accuracy is critical.
  • Your suggestion for a better translation would be highly appreciated.

Original Document in Korean

View Original Document (Korean)

Static Typed Languages (TS) vs Dynamic Typed Languages (JS): Different Approaches

HJ, Ph.D. / Software Architect
(js.seth.h@gmail.com)
Draft: Dec 2024 / Revised: Dec 2025

[Author’s Intent] This article was written to challenge the belief that code quality or stability is determined solely by the choice of type system, and to reveal what kinds of decisions and operational approaches type systems actually require in practice.

Executive Summary

  • Programming languages can be divided into two categories based on how they handle data types.
  • Static typing: Types must be specified in advance and strictly followed.
  • Dynamic typing: Types are not specified in advance and can change as the program runs.
  • Common claims (not always true in practice):
    • Advantages of static typing (TS): code safety, auto-completion, easy refactoring, prevention of bugs, etc.
    • Disadvantages of static typing (TS): lack of flexibility, complex type declarations, steep learning curve, etc.
    • Advantages of dynamic typing (JS): rapid prototyping, flexibility, concise code, etc.
    • Disadvantages of dynamic typing (JS): possible runtime errors, maintenance difficulties in large projects, etc.
  • Many developers misunderstand type systems.
  • A type is, narrowly, a system that determines the physical/logical structure of data values and how they are interpreted.
  • Types are fundamentally used to determine input/output formats in the general form of information processing: input-process-output.
  • This system is not as robust as it seems.
  • Since types are a “handle with care” item, the best way to use them differs for each system.
  • Therefore, the best way to leverage the strengths and overcome the weaknesses of each type system is different.
  • In static type languages, type checking is performed at compile time, so you should handle routine failures as much as possible through type checks.
  • In dynamic type languages, you must detect and handle exceptions due to lack of type information during development, testing, and operation, often outside the code itself.
  • However, this is not a matter of strict either/or.

General Understanding

The type system of a programming language is a fundamental concept in information processing. It defines the structure of variables, functions, objects, etc., and determines what operations are available for each, as well as the requirements for input/output for those operations.

Type systems are generally divided into static and dynamic types. Static type languages specify types clearly at code writing time and check them at compile time (e.g., TypeScript, Java, C#, Kotlin, Rust). Dynamic type languages do not require explicit type declarations, and types are determined at runtime (e.g., JavaScript, Python, Ruby, PHP).

While the pros and cons of each are often discussed, these are “expected effects” rather than strict causal relationships from the perspective of programming language theory.

  • Static type languages...
    • Make communication clear in large team projects.
    • Allow errors from code changes to be detected early.
    • But type declarations can become complex, flexibility is reduced, and the learning curve is steep.
    • The type system can even become a hindrance when requirements change rapidly.
  • Dynamic type languages...
    • Enable rapid prototyping, high flexibility, and concise code.
    • But runtime errors are more likely, and maintenance can be difficult in large projects.
    • As code complexity increases, bugs can hide more easily, and new team members may take longer to understand the code.

The Reality of Modern Computer Systems

Many developers misunderstand type systems.

Let’s look at the physical aspects of modern computers. From a software engineer’s perspective, semiconductors are physically hardcoded machine code processing engines.

Software runs on top of these machine code engines, and the type system is essentially an application of the forms of data that the hardware can handle. Thus, in modern deterministic systems (Turing machines), the type system is a fundamental element of information processing.

At the hardware level, operations are determined by the physical structure of the data (integer or floating point, etc.), and this builds up to first-level logical structures (strings, etc.), second-level logical structures (user-defined types), and third-level logical structures (user customization at the solution/service level). All operations/routines are limited by the data formats they can handle.

Therefore, even in dynamic languages, there are always restrictions on input formats. For example, if you code obj.a / obj.b, both a and b must be arithmetic, and b must not be zero. Dynamic languages simply do not declare and check these at compile time. That’s why the term is “dynamic type system,” not “no type system.”

On the other hand, dynamic languages can introduce schema validation routines (like JSON-schema), and static languages can bypass type checks by exchanging data as key-value dictionaries. Examples abound:

  • In Java, exchanging dictionaries to handle variable data is common in practice.
  • MongoDB, known as a schemaless database, provides schema validation features.
  • In Python, using typing.Any or dict to bypass type checks is common in API design.
  • In TypeScript, patterns like as any or Record<string, unknown> are often used to disable type checks.
  • NoSQL databases like DynamoDB and CouchDB are schemaless, but schema validation is often added at the application level using JSON Schema, Joi, etc.
  • Many APIs handle input defensively, regardless of the type system.

As you can see, type systems are not as powerful as they seem and can be bypassed depending on coding style. Thus, predicting pros and cons based solely on the type system is a hasty generalization.

The type system of a programming language is just its “default” way of handling types, not a guarantee of the resulting software’s data handling policy.

Respect the Type System – Static Typing

Of course, when a language adopts a type system, it is designed to leverage its characteristics. This is not just for individual code execution, but for the entire ecosystem built with that language.

In static typing, it is best to detect type deficiencies at compile time. But what is a type deficiency? It’s not just putting a string where an int is required—such cases are impossible in static typing. Instead, it’s about ensuring that routines have the requirements needed for correct operation.

For example, suppose routine F takes Type A as input. Often, A’s default constructor is allowed, but this may mean A can be instantiated without being ready for computation. To make type deficiencies compile-time errors, you must restrict A’s constructor (e.g., by hardcoding valid inputs or using factory methods like Java Lombok’s of()), preventing instantiation of unusable A.

Another example is handling divide-by-zero errors. Formally, you require a nonzero value, but many handle this by throwing a runtime error. However, this is runtime checking, not static typing. Defining and using a custom type like NotZeroFloat or NotZeroInt is the static typing way, though it’s rarely practical.

Conversely, you can lose the benefits of static typing by using key-value dictionaries or dynamic objects at the core of your architecture. For example, if a Java web server framework provides all request data as nested Dictionary<String, Object>, you get no benefit from static typing. Such patterns should be avoided or used only at the system’s periphery.

As shown, the goal of static typing is to replace as many runtime errors as possible with static analysis errors, reducing the need for runtime validation. Thus, you should distinguish what to do and what to avoid, making your routines’ failures as type-checkable as possible, and actively avoiding patterns that undermine this. This is how to maximize the benefits of static typing, even if it sometimes reduces productivity.

Respect the Type System – Dynamic Typing

So, how do you respect the type system in dynamic languages?

Dynamic languages do not perform static analysis to check if routines can handle given data. Many think this means there are no type checks, but as long as you use hardware and machine code, types always exist. Thus, in dynamic typing, type compliance is checked at runtime—through QA/QC or TDD.

If static languages rely on compiler-provided static analysis, dynamic languages rely on runtime analysis during execution. This is natural.

However, remember that all programs have data format requirements, and the primary goal of software is to provide promised services. Identifying abnormal data is a secondary concern. Excessive defensive coding for the second goal can waste development resources, so broad, lenient handling is often preferable. Otherwise, you lose the advantages of dynamic typing.

Practically, it is necessary to introduce strong schema validation (e.g., JSON-schema) at the system’s external interfaces. Once data is inside the system and refined, you can trust it, but incoming data must be cleansed.

Garbage in, Garbage out

Thus, in dynamic typing, compliance with data formats should be handled not just in code, but also through development methodology, organizational processes, and clear designation of data cleansing zones.

Conclusion: Is It a Binary Choice?

Is it a strict choice between static typing and external solutions?

The answer is no. Divide-by-zero and out-of-index errors are runtime type violations that are hard to catch statically, and dynamic typing relies on data trust, but you can’t always trust external data. In reality, even static typing sometimes needs variable data, and dynamic typing sometimes needs strong constraints.

Ultimately, the type system is just the language’s default. But using the default well—minimizing use of the opposite approach—yields the best results in the long run. So, it’s important to distinguish what follows the default policy and what doesn’t, and use each type system accordingly.

See Also

Author

HJ, Ph.D. / Software Architect
(js.seth.h@gmail.com)
https://js-seth-h.github.io/website/Biography/

Over 20 years in software development, focusing on design reasoning and system structure – as foundations for long-term productivity and structural clarity.

Researched semantic web and meta-browser architecture in graduate studies,
with an emphasis on structural separation between data and presentation.

Ph.D. in Software, Korea University
M.S. in Computer Science Education, Korea University
B.S. in Computer Science Education, Korea University