- Primitive Types
- Structured Data Type Definitions (Dataclasses, Dictionaries, TypedDict)
- Classes
- Class Property Accessors
- Class Generics
- Class Inheritance
- Enum Definitions
- Type Casting
Data-Types
Change TopicGo Data-Types
TypeScript Data-Types
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
).
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 };
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 tofinally
in other languages but at function scope)
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.
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 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 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 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 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" }
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..."
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] }
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;
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
toint
) 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 thati
holds astring
.- 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 tov
and checks if the assertion succeeded (ok
). - This pattern is commonly used when working with interfaces.
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!