Every TypeScript program is built from a small set of primitive types. Master these and you can read almost any TypeScript code. In this lesson we cover the core primitives — string, number, and boolean — plus two special types, any and unknown, that control how strict the compiler is when it cannot infer a type.
TypeScript uses the same primitive values as JavaScript and gives each a type name:
let username: string = "codeyourcraft";
let score: number = 98.6; // all numbers: ints, floats, hex, binary
let isActive: boolean = true;
let big: bigint = 9007199254740993n;
let key: symbol = Symbol("id");Note that the type names are lowercase: string, not String. The capitalized versions refer to JavaScript wrapper objects and should almost never appear in your annotations.
TypeScript is smart about inferring types from initial values. These two lines are equivalent in practice:
let city: string = "Berlin"; // explicit
let town = "Munich"; // inferred as string
town = 42; // Error: Type number is not assignable to type stringA good rule of thumb: let inference do the work for local variables with obvious initial values, and write explicit annotations for function parameters, return types, and public APIs where clarity matters.
JavaScript has two "empty" values, and TypeScript models both. With the strictNullChecks compiler option enabled (part of strict mode, which you should always use), you cannot assign null or undefined to a variable unless its type explicitly allows it:
let nickname: string | null = null; // union type: string OR null
nickname = "Sam"; // fine
let title: string = null; // Error under strictNullChecksThis single feature eliminates a huge class of "cannot read property of undefined" runtime crashes.
any turns off type checking for a value. You can assign anything to it and call anything on it:
let data: any = fetchLegacyValue();
data.foo.bar.baz; // compiles, but may crash at runtime
data(); // also compiles!any is contagious — once a value is any, everything derived from it is unchecked too. It exists for migration from JavaScript and for rare interop cases, but every any is a hole in your safety net. Enable the noImplicitAny compiler option so the compiler at least forces you to write any deliberately instead of getting it by accident.
unknown also accepts any value, but it flips the rules: you cannot use an unknown value until you narrow it to a concrete type:
let input: unknown = JSON.parse(rawText);
input.toUpperCase(); // Error: input is of type unknown
if (typeof input === "string") {
console.log(input.toUpperCase()); // OK: narrowed to string
}Use unknown for values from the outside world — JSON payloads, user input, third-party callbacks — and prove their shape with typeof, instanceof, or validation before touching them. It gives you the flexibility of any with none of the danger.
Two more types round out the picture. void is the return type of functions that return nothing, and never marks values that cannot exist — for example the return type of a function that always throws. You will meet never again when we cover narrowing and exhaustiveness checks.
string, number, boolean, bigint, and symbol — always lowercase.null and undefined must be opted into via union types.any disables checking entirely; treat it as a last resort.unknown for untrusted data and narrow it before use.Next lesson: Functions and Type Annotations — typing parameters, return values, defaults, and rest arguments.