Data-Types

Change Topic
  • Primitive Types
  • Structured Data Type Definitions (Dataclasses, Dictionaries, TypedDict)
  • Classes
  • Class Property Accessors
  • Class Generics
  • Class Inheritance
  • Enum Definitions
  • Type Casting

TypeScript Data-Types

Go Data-Types


Structured Data Type Definitions (Interfaces, Types)

TypeScript allows defining structured data with interface or type. Fields can be optional (?).

interface User { id: number; name: string; email?: string; // Optional field } const user: User = { id: 1, name: "Alice" };

Alternatively, using type:

type Product = { id: number; name: string; price: number; category?: string; // Optional field }; const item: Product = { id: 101, name: "Laptop", price: 1200 };

Structured Data Type Definitions (Structs)

Golang structures data using structs, with optional fields using pointers.

type User struct { ID int Name string Email *string // Optional field using a pointer } func main() { email := "alice@example.com" user := User{ID: 1, Name: "Alice", Email: &email} fmt.Println(user) }
  • struct defines structured data.
  • Optional fields use pointers (*string).

Classes

class Person { name: string; constructor(name: string) { this.name = name; console.log(`${this.name} is created`); } greet(): void { console.log(`Hello, my name is ${this.name}`); } // No direct destructor, but `finalize` can be used in specific environments } const person = new Person("Alice"); person.greet(); // Output: // Alice is created // Hello, my name is Alice

Typescript does not have destructors.


Classes

type Person struct { Name string } // Constructor function (Go does not have a built-in constructor) func NewPerson(name string) *Person { fmt.Println(name, "is created") return &Person{Name: name} } // Method attached to struct func (p *Person) Greet() { fmt.Println("Hello, my name is", p.Name) } // Destructor simulation using `defer` func DeletePerson(p *Person) { fmt.Println(p.Name, "is deleted") } func main() { person := NewPerson("Alice") person.Greet() // Simulating destructor with `defer` defer DeletePerson(person) }
  • No real classes, just the ability to associate a function with a structured type
  • Destructor function via defer keyword - this defers execution until the function has completed (similar to finally in other languages but at function scope)

Class Property Accessors

class Person { private _name: string; private _age: number; constructor(name: string, age: number) { this._name = name; this._age = age; } // Getter for name get name(): string { return this._name; } // Setter for name set name(newName: string) { if (newName.length < 3) { throw new Error("Name must be at least 3 characters long."); } this._name = newName; } } // Usage const person = new Person("Alice", 25); console.log(person.name); // Getter: Alice person.name = "Bob"; // Setter console.log(person.name); // Getter: Bob

Class Property Accessors

Interfaces don't have property getters and setters, but the interface and struct can be separated with property wrappers as functions:

type NamedEntity interface { GetName() string SetName(newName string) } type Person struct { name string } // Implementing the interface func (p *Person) GetName() string { return p.name } func (p *Person) SetName(newName string) { p.name = newName } func main() { var entity NamedEntity = &Person{name: "Charlie"} fmt.Println("Before:", entity.GetName()) entity.SetName("David") fmt.Println("After:", entity.GetName()) }

Class Generics

class Box<T> { private content: T; constructor(content: T) { this.content = content; } getContent(): T { return this.content; } } const intBox = new Box<number>(10); console.log(intBox.getContent()); // 10 const strBox = new Box<string>("TypeScript"); console.log(strBox.getContent()); // "TypeScript"

Class or Struct Generics

type Box[T any] struct { Content T } func main() { intBox := Box[int]{Content: 10} fmt.Println(intBox.Content) // 10 strBox := Box[string]{Content: "Golang"} fmt.Println(strBox.Content) // Golang }

Class Inheritance

class Animal { name: string; constructor(name: string) { this.name = name; } makeSound(): void { console.log("Some generic animal sound"); } } class Dog extends Animal { constructor(name: string) { super(name); // Calls parent constructor } makeSound(): void { console.log("Woof!"); } } const dog = new Dog("Buddy"); dog.makeSound(); // "Woof!"

Classes can also implement interface (pure abstract class):

interface Speakable { speak(): void; } class Robot implements Speakable { speak(): void { console.log("Beep boop!"); } } const bot = new Robot(); bot.speak(); // "Beep boop!"

Abstract classes also supported, with a parial implementation:

abstract class Vehicle { abstract move(): void; // Must be implemented by subclass stop(): void { console.log("Stopping..."); } } class Car extends Vehicle { move(): void { console.log("Driving..."); } } const car = new Car(); car.move(); // "Driving..." car.stop(); // "Stopping..."

Class Inheritance

Using struct embedding to emulate inheritance:

type Animal struct { Name string } func (a Animal) MakeSound() { fmt.Println("Some generic animal sound") } // Dog "inherits" from Animal via embedding type Dog struct { Animal } func (d Dog) MakeSound() { fmt.Println("Woof!") } func main() { dog := Dog{Animal{Name: "Buddy"}} dog.MakeSound() // "Woof!" }

Interface is supported for structs:

type Speakable interface { Speak() } type Robot struct{} func (r Robot) Speak() { fmt.Println("Beep boop!") } func main() { var bot Speakable = Robot{} bot.Speak() // "Beep boop!" }

Base class constructors implemented via special functions that create object:

type Animal struct { Name string } func NewAnimal(name string) Animal { return Animal{Name: name} } type Dog struct { Animal } func NewDog(name string) Dog { return Dog{Animal: NewAnimal(name)} } func main() { dog := NewDog("Buddy") fmt.Println(dog.Name) // "Buddy" }

Enum Definitions

Enums define a set of named constants.

enum Status { Pending, InProgress, Completed, } const currentStatus: Status = Status.InProgress;
  • Enum values default to numeric (starting from 0).
  • Custom values can be assigned:
enum Role { User = "USER", Admin = "ADMIN", } const userRole: Role = Role.Admin;

Enum Definitions (Using iota)

Golang does not have native enums but uses const with iota for enumerations.

type Status int const ( Pending Status = iota InProgress Completed ) func main() { currentStatus := InProgress fmt.Println(currentStatus) // Output: 1 }
  • iota generates sequential integer values automatically.
  • Explicit string-based enums require custom methods.
func (s Status) String() string { return [...]string{"Pending", "In Progress", "Completed"}[s] }

Type Casting in TypeScript

TypeScript provides mechanisms for type casting to convert between types or assert the type of a value. This is useful when working with dynamic data or narrowing types.

Explicit Type Casting. TypeScript allows explicit type casting using the as keyword or angle-bracket syntax (<>).

let value: unknown = "Hello, TypeScript"; // Casting to a string let strLength: number = (value as string).length; console.log(strLength); // Output: 17 // Alternatively, using angle-bracket syntax let strLengthAlt: number = (<string>value).length; console.log(strLengthAlt); // Output: 17
  • Use as for readability and compatibility with JSX.
  • Type casting does not perform runtime type checks; it only tells the compiler to treat the value as a specific type.

Type guards are used to narrow down the type of a variable within a block of code. They are often used with typeof, instanceof, or custom type-checking functions.

function processValue(value: string | number): void { if (typeof value === "string") { console.log(`String value: ${value.toUpperCase()}`); } else { console.log(`Number value: ${value * 2}`); } } processValue("hello"); // Output: String value: HELLO processValue(42); // Output: Number value: 84
class Animal { makeSound(): void { console.log("Some generic animal sound"); } } class Dog extends Animal { makeSound(): void { console.log("Woof!"); } } function identifyAnimal(animal: Animal): void { if (animal instanceof Dog) { console.log("This is a dog"); } else { console.log("This is some other animal"); } } const dog = new Dog(); identifyAnimal(dog); // Output: This is a dog

Type assertions are used to tell the TypeScript compiler to treat a value as a specific type. This is useful when you know more about the type than the compiler does.

let someValue: unknown = "TypeScript"; // Assert that the value is a string let strLength: number = (someValue as string).length; console.log(strLength); // Output: 10

You can combine type assertions or type guards with conditional statements to handle dynamic types.

function printValue(value: string | number): void { if (typeof value === "string") { console.log(`String: ${value}`); } else { console.log(`Number: ${value}`); } } printValue("Hello"); // Output: String: Hello printValue(123); // Output: Number: 123

You can define custom type guard functions to narrow down types.

interface Cat { meow(): void; } interface Dog { bark(): void; } function isCat(animal: Cat | Dog): animal is Cat { return (animal as Cat).meow !== undefined; } function handleAnimal(animal: Cat | Dog): void { if (isCat(animal)) { animal.meow(); } else { animal.bark(); } } const cat: Cat = { meow: () => console.log("Meow!") }; const dog: Dog = { bark: () => console.log("Woof!") }; handleAnimal(cat); // Output: Meow! handleAnimal(dog); // Output: Woof!

Type Casting

Go provides mechanisms for type conversion and type assertion. These allow you to convert between compatible types or check and extract the underlying type of an interface.

Type Conversion (Base Type Casting) - in Go, type casting is done explicitly using type conversion syntax. This is used to convert between compatible types, such as int to float64.

func main() { var a int = 42 var b float64 = float64(a) // Convert int to float64 var c int = int(b) // Convert float64 back to int fmt.Println(a, b, c) // Output: 42 42.0 42 }
  • Type conversion is explicit and must be specified by the developer.
  • Incompatible types (e.g., string to int) cannot be converted directly.

Type assertion is used to extract the concrete value of an interface. It checks whether the interface holds a specific type.

func main() { var i interface{} = "hello" // Assert that the interface holds a string s, ok := i.(string) if ok { fmt.Println("String value:", s) // Output: String value: hello } else { fmt.Println("Not a string") } }
  • i.(string) asserts that i holds a string.
  • The second return value (ok) is a boolean indicating whether the assertion succeeded.

The type switch statement is used to handle multiple types in a single block. It is particularly useful when working with interfaces.

func describe(i interface{}) { switch v := i.(type) { case int: fmt.Println("Integer:", v) case string: fmt.Println("String:", v) case bool: fmt.Println("Boolean:", v) default: fmt.Println("Unknown type") } } func main() { describe(42) // Output: Integer: 42 describe("hello") // Output: String: hello describe(true) // Output: Boolean: true describe(3.14) // Output: Unknown type }
  • The type switch uses .(type) to determine the type of the value held by an interface.
  • Each case handles a specific type.

Go allows you to perform type assertion and assignment in a single if statement. This is useful for concise type checking.

func main() { var i interface{} = 42 if v, ok := i.(int); ok { fmt.Println("Integer value:", v) // Output: Integer value: 42 } else { fmt.Println("Not an integer") } }
  • The if statement assigns the value to v and checks if the assertion succeeded (ok).
  • This pattern is commonly used when working with interfaces.