"Static" and "dynamic" typing are often described as though dynamically typed languages don't have types at all, but that's not accurate — Python and JavaScript values absolutely have types at runtime. The real distinction is when type checking happens: statically typed languages check types before the program runs, at compile time; dynamically typed languages check types while the program is running, at the moment an operation is actually attempted.

C# is statically typed: every variable's type is declared, or inferred by the compiler, before the program ever executes, and the compiler rejects a program that tries to use a type incorrectly — passing a string where an int is required fails to compile, full stop, regardless of whether that specific code path would ever have run in practice.

Python and JavaScript are dynamically typed: a variable doesn't have a declared type at all, only the value currently assigned to it does, and that value's type is checked only at the moment an operation actually happens on it. Code that would be a type error can sit in a Python file, never executed because a particular branch never runs, and the interpreter will never object — there's no compile-time pass checking every possible path the way a static compiler does.

This difference shows up concretely in how each language stores a value internally. In CPython, every value — even a simple integer — is a full Python object carrying its own type information alongside its data, which is part of why raw arithmetic in Python is slower than in a statically typed, compiled language: the interpreter has to check the object's type at runtime before it can decide which operation to actually perform. A C# int, by contrast, is a fixed-size value the compiler already knows the type of, so no runtime type check is needed to add two of them.

Dynamic typing's real cost isn't speed alone — it's that an entire category of bugs static typing catches automatically becomes a runtime risk instead. A typo in a dynamically typed language, like calling a method that doesn't exist on an object, only surfaces when that exact line actually executes, which might be a rarely hit branch that ships to production before anyone hits it. A statically typed compiler catches the same typo before the program ever runs, because it checks every declared type against every usage, not just the paths a particular test happened to exercise.

Neither approach is strictly better; they're different trade-offs between flexibility during development and guarantees before deployment, and each language's own documentation — Python's data model docs, the ECMAScript specification behind JavaScript, and Microsoft's C# language reference — describes the exact typing rules its runtime enforces, well past what a single comparison article can cover.