Complete Guide to Data Types in Programming

Introduction to Data Types

Data types are fundamental concepts in programming that define the type of data a variable can hold and the operations that can be performed on it. Understanding data types is crucial for writing correct, efficient, and bug-free code. Different programming languages approach data typing differently, but the core concepts remain consistent across languages.

Key Concepts

  • Type Safety: Prevents operations on incompatible data types
  • Static vs Dynamic: When types are checked (compile-time vs runtime)
  • Strong vs Weak: How strictly types are enforced
  • Primitive vs Complex: Built-in types vs user-defined types
  • Implicit vs Explicit: Automatic vs manual type conversion

1. Classification of Data Types

By Mutability

# Immutable types (cannot be changed after creation)
number = 42           # int - immutable
text = "hello"        # str - immutable
tuple_data = (1, 2)   # tuple - immutable
# Modifying immutable types creates new objects
text = text + " world"  # New string created
# Mutable types (can be changed in-place)
list_data = [1, 2, 3]  # list - mutable
dict_data = {"a": 1}   # dict - mutable
set_data = {1, 2, 3}   # set - mutable
list_data.append(4)     # Modifies original list
// JavaScript mutability
// Primitives are immutable
let str = "hello";
str.toUpperCase();  // Returns new string, doesn't modify original
// Objects are mutable
let obj = { name: "Alice" };
obj.name = "Bob";  // Modifies original object
let arr = [1, 2, 3];
arr.push(4);  // Modifies original array

By Storage

// Stack vs Heap allocation
#include <stdio.h>
#include <stdlib.h>
int main() {
// Stack allocation (fixed size, automatic)
int stack_int = 42;           // 4 bytes on stack
char stack_char = 'A';        // 1 byte on stack
int stack_array[10];          // 40 bytes on stack
// Heap allocation (dynamic, manual)
int* heap_int = malloc(sizeof(int));
*heap_int = 42;                // 4 bytes on heap
char* heap_string = malloc(100); // 100 bytes on heap
free(heap_int);               // Must free heap memory
free(heap_string);
return 0;
}

2. Primitive Data Types

Numeric Types

# Python numeric types
integer = 42                    # int (unlimited precision)
float_num = 3.14159            # float (double precision)
complex_num = 2 + 3j           # complex
# Numeric operations
print(10 // 3)    # 3 (integer division)
print(10 / 3)     # 3.33333 (float division)
print(10 % 3)     # 1 (modulo)
print(2 ** 3)     # 8 (exponentiation)
print(abs(-5))    # 5 (absolute value)
print(round(3.14159, 2))  # 3.14 (rounding)
// JavaScript numeric types
let integer = 42;              // Number (64-bit floating point)
let float = 3.14159;          // Number
let bigInt = 9007199254740991n; // BigInt (arbitrary precision)
let infinity = Infinity;       // Infinity
let nan = NaN;                 // Not a Number
// Number limitations
console.log(0.1 + 0.2);       // 0.30000000000000004 (precision issue)
console.log(Number.MAX_SAFE_INTEGER);  // 9007199254740991
// C numeric types
#include <stdint.h>
#include <float.h>
int main() {
// Integer types
char c = 127;                 // 1 byte (-128 to 127)
unsigned char uc = 255;       // 1 byte (0 to 255)
short s = 32767;              // 2 bytes (-32,768 to 32,767)
int i = 2147483647;           // 4 bytes (-2^31 to 2^31-1)
long l = 9223372036854775807; // 8 bytes (on 64-bit)
// Fixed-size integers (C99)
int8_t i8 = 127;
uint16_t u16 = 65535;
int32_t i32 = 2147483647;
uint64_t u64 = 18446744073709551615ULL;
// Floating point
float f = 3.14159f;           // 4 bytes, ~7 decimal digits
double d = 3.141592653589793; // 8 bytes, ~15 decimal digits
return 0;
}

Boolean Type

# Python boolean
is_active = True
is_deleted = False
# Boolean operations
print(True and False)   # False
print(True or False)    # True
print(not True)         # False
# Truthy/Falsy values
print(bool(0))          # False
print(bool(1))          # True
print(bool(""))         # False
print(bool("hello"))    # True
print(bool([]))         # False
print(bool([1, 2]))     # True
// JavaScript boolean
let isActive = true;
let isDeleted = false;
// Truthy/Falsy
console.log(Boolean(0));        // false
console.log(Boolean(""));       // false
console.log(Boolean(null));     // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN));      // false
console.log(Boolean(1));        // true
console.log(Boolean("hello"));  // true
console.log(Boolean([]));       // true (object)
console.log(Boolean({}));       // true

Character Type

// C character types
#include <stdio.h>
#include <wchar.h>
int main() {
// ASCII characters
char ch = 'A';
char newline = '\n';
char tab = '\t';
// Escape sequences
char quote = '\'';
char backslash = '\\';
char hex = '\x41';  // 'A' in hex
// Wide characters (Unicode)
wchar_t wch = L'世';
wprintf(L"%lc\n", wch);
return 0;
}

String Type

# Python strings
single = 'Hello'
double = "World"
multi_line = """This is
a multi-line
string"""
# String operations
name = "Alice"
greeting = f"Hello, {name}!"          # f-string
length = len(name)                     # 5
uppercase = name.upper()               # "ALICE"
lowercase = name.lower()               # "alice"
parts = "a,b,c".split(",")            # ["a", "b", "c"]
joined = "-".join(["a", "b", "c"])    # "a-b-c"
// JavaScript strings
let single = 'Hello';
let double = "World";
let template = `Hello, ${name}!`;      // Template literal
let multiline = `This is
a multi-line
string`;
// String methods
let str = "Hello World";
console.log(str.length);               // 11
console.log(str.toUpperCase());        // "HELLO WORLD"
console.log(str.includes("World"));    // true
console.log(str.slice(0, 5));          // "Hello"
console.log(str.split(" "));           // ["Hello", "World"]

3. Composite Data Types

Arrays/Lists

# Python lists (dynamic arrays)
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True]
# List operations
numbers.append(6)           # Add to end
numbers.insert(0, 0)        # Insert at position
numbers.extend([7, 8])      # Extend with list
popped = numbers.pop()      # Remove last
numbers.remove(3)           # Remove first occurrence
index = numbers.index(5)    # Find index
numbers.sort()              # Sort in-place
reversed = numbers[::-1]    # Reverse
# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
// JavaScript arrays
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "hello", true, null];
// Array operations
numbers.push(6);           // Add to end
numbers.unshift(0);        // Add to beginning
let last = numbers.pop();  // Remove last
let first = numbers.shift(); // Remove first
numbers.splice(2, 1);      // Remove at index
let sliced = numbers.slice(1, 3);
let doubled = numbers.map(n => n * 2);
let evens = numbers.filter(n => n % 2 === 0);
let sum = numbers.reduce((a, b) => a + b, 0);

Tuples

# Python tuples (immutable sequences)
point = (10, 20)
coordinates = (x, y, z) = (1, 2, 3)
single_element = (42,)  # Trailing comma required
# Tuple operations
print(point[0])          # Access by index
print(len(point))        # Length
print(point + (30,))     # Concatenation
print(point * 2)         # Repetition
print(10 in point)       # Membership
// TypeScript tuples (fixed length, typed)
let point: [number, number] = [10, 20];
let user: [string, number, boolean] = ["Alice", 30, true];
// Accessing tuple elements
console.log(point[0]);     // 10
console.log(user[1]);      // 30
// Tuple with optional elements
let optional: [string, number?] = ["Hello"];  // Valid

Dictionaries/Maps/Objects

# Python dictionaries
user = {
"name": "Alice",
"age": 30,
"email": "[email protected]"
}
# Dictionary operations
user["city"] = "New York"          # Add/update
name = user.get("name")             # Safe access
age = user.pop("age")               # Remove and return
del user["email"]                   # Delete key
# Dictionary iteration
for key in user:
print(f"{key}: {user[key]}")
for key, value in user.items():
print(f"{key}: {value}")
# Dictionary comprehension
squares = {x: x**2 for x in range(5)}
// JavaScript objects
let user = {
name: "Alice",
age: 30,
email: "[email protected]"
};
// Access and modification
user.city = "New York";           // Dot notation
user["phone"] = "123-4567";        // Bracket notation
delete user.email;                 // Delete property
// Object iteration
for (let key in user) {
console.log(`${key}: ${user[key]}`);
}
Object.keys(user).forEach(key => {
console.log(`${key}: ${user[key]}`);
});
// Map object (preserves insertion order)
let map = new Map();
map.set("name", "Alice");
map.set("age", 30);
map.get("name");  // "Alice"

Sets

# Python sets (unordered, unique elements)
numbers = {1, 2, 3, 4, 5}
mixed = {1, "hello", 3.14}
# Set operations
numbers.add(6)                      # Add element
numbers.remove(3)                   # Remove element
numbers.discard(10)                 # Remove if exists
popped = numbers.pop()              # Remove arbitrary
# Set operations with other sets
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
union = set_a | set_b              # {1, 2, 3, 4, 5, 6}
intersection = set_a & set_b        # {3, 4}
difference = set_a - set_b          # {1, 2}
symmetric_diff = set_a ^ set_b      # {1, 2, 5, 6}
// JavaScript Set
let numbers = new Set([1, 2, 3, 4, 5]);
numbers.add(6);
numbers.delete(3);
numbers.has(4);          // true
numbers.size;            // 5
// Set operations (need to implement)
let setA = new Set([1, 2, 3, 4]);
let setB = new Set([3, 4, 5, 6]);
let union = new Set([...setA, ...setB]);
let intersection = new Set([...setA].filter(x => setB.has(x)));
let difference = new Set([...setA].filter(x => !setB.has(x)));

Structs/Records

// C structs
#include <stdio.h>
#include <string.h>
struct Person {
char name[50];
int age;
float height;
};
struct Point {
int x;
int y;
};
int main() {
struct Person person1 = {"Alice", 30, 5.7};
struct Person person2;
strcpy(person2.name, "Bob");
person2.age = 25;
person2.height = 6.0;
printf("Name: %s, Age: %d\n", person1.name, person1.age);
return 0;
}
// Rust structs
struct Person {
name: String,
age: u32,
height: f64,
}
struct Point(i32, i32);  // Tuple struct
struct Unit;  // Unit struct
fn main() {
let person = Person {
name: String::from("Alice"),
age: 30,
height: 5.7,
};
let point = Point(10, 20);
println!("Name: {}, Age: {}", person.name, person.age);
println!("Point: ({}, {})", point.0, point.1);
}

4. Type Systems

Static Typing

// Java - statically typed
public class StaticTyping {
public static void main(String[] args) {
int number = 42;           // Type declared
String text = "Hello";      // Type inferred? No, declared
number = 100;               // OK - same type
// number = "text";         // Compilation error
// text = 42;               // Compilation error
}
}
// TypeScript - statically typed with type inference
let number = 42;           // Type inferred as number
let text: string = "Hello"; // Explicit type
let mixed: number | string = "can be string or number";
// Type checking
function add(a: number, b: number): number {
return a + b;
}
add(5, 3);      // OK
// add("5", 3);  // Type error

Dynamic Typing

# Python - dynamically typed
x = 42          # x is int
x = "hello"     # x is now str - perfectly valid
x = [1, 2, 3]   # x is now list
# Type checking at runtime
def add(a, b):
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
return a + b
elif isinstance(a, str) and isinstance(b, str):
return a + b
else:
raise TypeError("Unsupported types")
print(add(5, 3))        # 8
print(add("Hello", "World"))  # HelloWorld
# print(add(5, "3"))    # Raises TypeError
// JavaScript - dynamically typed
let x = 42;          // number
x = "hello";         // string - valid
x = [1, 2, 3];       // array - valid
// Type coercion
console.log(5 + "3");      // "53" (string concatenation)
console.log(5 - "3");      // 2 (numeric subtraction)
console.log("5" * "3");    // 15 (numeric multiplication)
console.log(true + true);  // 2 (true converted to 1)

Strong vs Weak Typing

# Python - strong typing
# No implicit type conversion
x = 5
y = "10"
# print(x + y)      # TypeError: unsupported operand type(s)
# Explicit conversion required
result = x + int(y)  # 15
// JavaScript - weak typing (lots of implicit conversions)
console.log(5 + "3");        // "53" - number coerced to string
console.log("5" - 3);        // 2 - string coerced to number
console.log(true + true);    // 2 - boolean coerced to number
console.log([] + []);        // "" - arrays coerced to strings
console.log([] + {});        // "[object Object]"
console.log({} + []);        // 0 (in some cases)

5. Type Conversion

Implicit Conversion (Coercion)

// JavaScript implicit coercion
console.log("5" + 3);        // "53"
console.log("5" - 3);        // 2
console.log(true + true);    // 2
console.log(false + "a");    // "falsea"
// Loose equality (==) performs coercion
console.log(5 == "5");       // true
console.log(false == 0);     // true
console.log(null == undefined); // true
// Strict equality (===) doesn't coerce
console.log(5 === "5");      // false
console.log(false === 0);    // false

Explicit Conversion (Casting)

# Python explicit conversion
x = 42
print(str(x))        # "42"
print(float(x))      # 42.0
print(hex(x))        # "0x2a"
print(bin(x))        # "0b101010"
y = "3.14"
print(int(float(y))) # 3
print(float(y))      # 3.14
z = "hello"
print(list(z))       # ['h', 'e', 'l', 'l', 'o']
print(tuple(z))      # ('h', 'e', 'l', 'l', 'o')
// JavaScript explicit conversion
let x = 42;
console.log(String(x));     // "42"
console.log(x.toString());  // "42"
console.log(Number("42"));  // 42
console.log(parseInt("42px")); // 42
console.log(parseFloat("3.14px")); // 3.14
console.log(Boolean(0));    // false
let arr = [1, 2, 3];
console.log(JSON.stringify(arr)); // "[1,2,3]"

Type Checking

# Python type checking
x = 42
print(type(x))          # <class 'int'>
print(isinstance(x, int))  # True
print(isinstance(x, (int, float)))  # True
def process(data):
if isinstance(data, str):
return data.upper()
elif isinstance(data, list):
return [str(x) for x in data]
elif isinstance(data, dict):
return {k: str(v) for k, v in data.items()}
else:
return str(data)
// JavaScript type checking
let x = 42;
console.log(typeof x);           // "number"
console.log(Array.isArray([]));   // true
console.log(x instanceof Number); // false (primitive)
console.log(null === null);       // true
console.log(typeof null);         // "object" (JavaScript quirk)

6. Null and Undefined

Python

# Python - None
value = None
if value is None:
print("Value is None")
# None is a singleton
x = None
y = None
print(x is y)  # True
# Default values
def greet(name=None):
if name is None:
print("Hello, World!")
else:
print(f"Hello, {name}!")

JavaScript

// JavaScript - null and undefined
let undefinedVar;        // undefined
let nullVar = null;      // null
console.log(typeof undefined);  // "undefined"
console.log(typeof null);       // "object" (quirk)
// Difference
console.log(undefined == null);   // true (loose equality)
console.log(undefined === null);  // false (strict equality)
// Optional chaining
const user = { name: "Alice" };
console.log(user?.address?.city);  // undefined (no error)

C/C++

// C - NULL pointer
#include <stdio.h>
#include <stdlib.h>
int main() {
int* ptr = NULL;  // Null pointer
if (ptr == NULL) {
printf("Pointer is null\n");
}
// Dereferencing NULL causes crash
// *ptr = 42;  // Segmentation fault
return 0;
}

7. Custom Types

Enums

# Python enums
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
class Status(Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
# Usage
color = Color.RED
print(color.name)    # "RED"
print(color.value)   # 1
if color == Color.RED:
print("It's red!")
// JavaScript enums (using objects)
const Color = {
RED: Symbol('red'),
GREEN: Symbol('green'),
BLUE: Symbol('blue')
};
// Or with strings
const Status = {
PENDING: 'pending',
APPROVED: 'approved',
REJECTED: 'rejected'
} as const;
type StatusType = typeof Status[keyof typeof Status];
// Rust enums
enum Color {
Red,
Green,
Blue,
RGB(u8, u8, u8),  // Enum with data
}
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}

Type Aliases

# Python type aliases (type hints)
from typing import List, Dict, Union
UserId = int
UserMap = Dict[UserId, str]
def get_user_name(user_id: UserId, users: UserMap) -> str:
return users.get(user_id, "Unknown")
// TypeScript type aliases
type UserId = number;
type User = {
id: UserId;
name: string;
email: string;
};
type ApiResponse<T> = {
data: T;
status: number;
error?: string;
};
// Union types
type Status = "pending" | "approved" | "rejected";
type ID = string | number;

Classes

# Python classes
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def greet(self) -> str:
return f"Hello, I'm {self.name}"
def is_adult(self) -> bool:
return self.age >= 18
# Inheritance
class Student(Person):
def __init__(self, name: str, age: int, student_id: str):
super().__init__(name, age)
self.student_id = student_id
// JavaScript classes
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, I'm ${this.name}`;
}
isAdult() {
return this.age >= 18;
}
}
class Student extends Person {
constructor(name, age, studentId) {
super(name, age);
this.studentId = studentId;
}
}

8. Type Inference

# Python type hints (type inference in modern IDEs)
def add(a: int, b: int) -> int:
return a + b
# Type inference tools (mypy)
x = 42          # mypy infers int
y = "hello"     # mypy infers str
z = [1, 2, 3]   # mypy infers List[int]
// TypeScript type inference
let x = 42;              // inferred as number
let y = "hello";         // inferred as string
let z = [1, 2, 3];       // inferred as number[]
// Contextual typing
window.onmousedown = function(event) {
// event is inferred as MouseEvent
console.log(event.button);
};
// Return type inference
function add(a: number, b: number) {
return a + b;  // inferred as number
}
// Rust type inference
let x = 42;          // i32 (default integer type)
let y = 3.14;        // f64 (default float type)
let v = vec![1, 2];  // Vec<i32>
// Type annotations needed when ambiguous
let numbers: Vec<i32> = Vec::new();  // Must specify type
let result: u32 = "42".parse().unwrap();  // Type annotation required

9. Language-Specific Type Features

Python - Duck Typing

# Python duck typing - "If it walks like a duck..."
class Duck:
def quack(self):
return "Quack!"
class Person:
def quack(self):
return "I'm quacking like a duck!"
def make_it_quack(obj):
return obj.quack()  # Works with any object that has quack()
duck = Duck()
person = Person()
print(make_it_quack(duck))    # "Quack!"
print(make_it_quack(person))  # "I'm quacking like a duck!"

TypeScript - Structural Typing

// TypeScript structural typing
interface Point {
x: number;
y: number;
}
interface NamedPoint {
x: number;
y: number;
name: string;
}
// NamedPoint is compatible with Point (has at least x and y)
function printPoint(p: Point) {
console.log(`(${p.x}, ${p.y})`);
}
const np: NamedPoint = { x: 10, y: 20, name: "Origin" };
printPoint(np);  // Works! (structural compatibility)

Rust - Ownership System

// Rust ownership and borrowing
fn main() {
let s1 = String::from("hello");
let s2 = s1;  // s1 is moved to s2, s1 is no longer valid
// println!("{}", s1);  // Error: value borrowed after move
// Borrowing
let s3 = String::from("world");
let len = calculate_length(&s3);  // Borrow reference
println!("Length of '{}' is {}", s3, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}  // s goes out of scope, but nothing is dropped

C - Union Types

// C unions - same memory location for different types
#include <stdio.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 42;
printf("data.i: %d\n", data.i);      // 42
printf("data.f: %f\n", data.f);      // Undefined
data.f = 3.14;
printf("data.i: %d\n", data.i);      // Undefined
printf("data.f: %f\n", data.f);      // 3.14
return 0;
}

10. Common Pitfalls

Floating-Point Precision

# Floating-point precision issues
print(0.1 + 0.2)  # 0.30000000000000004 (not 0.3)
# Solution: use decimal
from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2'))  # 0.3
# Or use rounding
print(round(0.1 + 0.2, 2))  # 0.3
// JavaScript floating-point issues
console.log(0.1 + 0.2);  // 0.30000000000000004
// Solution: use epsilon
const epsilon = 0.000001;
console.log(Math.abs((0.1 + 0.2) - 0.3) < epsilon);  // true

Integer Overflow

// C integer overflow (undefined behavior)
#include <stdio.h>
#include <limits.h>
int main() {
int x = INT_MAX;  // 2,147,483,647
int y = x + 1;     // Undefined behavior (wraps to -2,147,483,648)
printf("%d\n", y);  // -2147483648
// Use unsigned for wrap-around
unsigned int ux = UINT_MAX;
unsigned int uy = ux + 1;  // Wraps to 0
printf("%u\n", uy);  // 0
return 0;
}

Reference vs Value Types

// JavaScript: primitives vs objects
// Primitives (passed by value)
let a = 5;
let b = a;
b = 10;
console.log(a);  // 5 (unchanged)
// Objects (passed by reference)
let obj1 = { value: 5 };
let obj2 = obj1;
obj2.value = 10;
console.log(obj1.value);  // 10 (changed)
// Copying objects
let obj3 = { ...obj1 };        // Shallow copy
let obj4 = JSON.parse(JSON.stringify(obj1));  // Deep copy
# Python: mutable vs immutable
# Immutable
a = 5
b = a
b = 10
print(a)  # 5
# Mutable
list1 = [1, 2, 3]
list2 = list1
list2.append(4)
print(list1)  # [1, 2, 3, 4] (changed)
# Copying
list3 = list1.copy()           # Shallow copy
import copy
list4 = copy.deepcopy(list1)   # Deep copy

Conclusion

Data types are fundamental to programming:

Key Takeaways

  1. Type Systems: Understand static vs dynamic, strong vs weak typing
  2. Primitive Types: Numbers, strings, booleans - building blocks
  3. Composite Types: Arrays, objects, structs - organizing data
  4. Type Safety: Prevents many common programming errors
  5. Type Conversion: Explicit is better than implicit
  6. Language-Specific: Each language has unique type features

Type System Comparison

FeaturePythonJavaScriptTypeScriptRustC/C++
TypingDynamicDynamicStaticStaticStatic
StrengthStrongWeakStrongStrongWeak
Type InferenceLimitedLimitedStrongStrongLimited
Null SafetyPartialNoYesYesNo
GenericsYesNoYesYesYes
Memory SafetyYesYesYesYesNo

Best Practices

  1. Be explicit with type conversions
  2. Use type hints/annotations when available
  3. Understand your language's type system
  4. Test edge cases (null, undefined, empty collections)
  5. Choose appropriate types for your data
  6. Be aware of precision issues with floating-point
  7. Consider mutability implications

Data types are the foundation of how programs store and manipulate information. Mastering them is essential for writing correct, efficient, and maintainable code!

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper