TypeScript - Типсовместимость

Здравствуйте, будущие маги кодирования! Сегодня мы отправимся в увлекательное путешествие в мир TypeScript и исследуем fascynating концепцию Типсовместимости. Не волнуйтесь, если вы новички в программировании - я буду вашим доброжелательным проводником, и мы рассмотрим эту тему шаг за шагом. Так что возьмите свои виртуальные палочки (клавиатуры), и давайте наведем некоторые заклинания TypeScript!

TypeScript - Type Compatibility

Как TypeScript выполняет проверки типсовместимости?

Представьте, что вы пытаетесьinsert diferentes bloques de formas diferentes en un agujero. Типсовместимость TypeScript немного похожа на это - это все о том, может ли один тип поместиться в другой. Но вместо физических форм мы имеем дело с типами данных.

Структурное типирование

TypeScript использует то, что мы называем "структурным типированием". Это означает, что он больше заботится о форме объекта, чем о его точном типе名称. Давайте посмотрим на пример:

interface Pet {
name: string;
}

class Dog {
name: string;
}

let pet: Pet;
let dog = new Dog();

pet = dog; // Это в порядке!

В этом магическом зоопарке TypeScript говорит: "Эй, у Pet и Dog есть свойство name типа string. Они look the same для меня, так что они совместимы!" Это как сказать, что square fitting peg в square hole, даже если один называется "square" и другой "block".

Утиное типирование

Есть забавная фраза в программировании: "Если оно ходит как утка и крякает как утка, то это必须是 утка." Это суть утиного типирования, и TypeScript принимает эту философию. Давайте посмотрим на это в действии:

interface Quacker {
quack(): void;
}

class Duck {
quack() {
console.log("Quack!");
}
}

class Person {
quack() {
console.log("I'm imitating a duck!");
}
}

let quacker: Quacker = new Duck(); // Очевидно, хорошо
quacker = new Person(); // Это также в порядке!

TypeScript не заботится о том, что Person неявно объявлен как Quacker. Ему только нужно, чтобы Person имел метод quack, как и Quacker. Так что и Duck, и Person совместимы с Quacker.

Как эффективно использовать типсовместимость?

Использование типсовместимости эффективно похоже на то, чтобы быть искусным решателем головоломок. Вот несколько советов:

1. Понимание проверок литералов объектов

TypeScript строже относится к литералам объектов. Давайте посмотрим почему:

interface Point {
x: number;
y: number;
}

let p: Point;

// Это в порядке
p = { x: 10, y: 20 };

// Это вызовет ошибку
p = { x: 10, y: 20, z: 30 };

TypeScript говорит: "Whoa there! Я просил Point, а ты даешь мне что-то extra (z). Это не разрешено!" Это помогает ловить потенциальные ошибки, где вы можете использовать объект неправильно.

2. Использование可选ательных свойств

Иногда вы хотите быть более гибким. Вот где на помощь приходят可选ательные свойства:

interface Options {
color?: string;
width?: number;
}

function configure(options: Options) {
// ...
}

configure({ color: "red" }); // Хорошо
configure({ width: 100 }); // Хорошо
configure({}); // Также хорошо!

Создавая свойства可选ательными (с помощью ?), вы говорите TypeScript: "It's cool if these aren't always there."

Функции и типсовместимость

Функции resemble Swiss Army knives of programming - они incredibly versatile. Давайте посмотрим, как типсовместимость работает с ними:

Совместимость параметров

TypeScript удивительно lenient с параметрами функции:

let x = (a: number) => 0;
let y = (b: number, s: string) => 0;

y = x; // Хорошо
x = y; // Ошибка

Это может показаться counterintuitive, но это безопасно. TypeScript говорит: "If you're expecting a function that takes two parameters, it's okay to give it a function that takes fewer. Extra parameter will just be ignored."

Совместимость типов возвращаемых значений

Типы возвращаемых значений также должны быть совместимыми:

let x = () => ({name: "Alice"});
let y = () => ({name: "Alice", location: "Wonderland"});

x = y; // Хорошо
y = x; // Ошибка

Можно возвращать больше, чем预期, но не меньше. Это как заказать пиццу и получить extra начинки бесплатно - это хорошо! Но если вы заказали пиццу с начинкой и получили только корж, вы были бы разочарованы.

Классы и типсовместимость

Классы resemble blueprints for objects, и они follow similar compatibility rules:

class Animal {
feet: number;
constructor(name: string, numFeet: number) { }
}

class Size {
feet: number;
}

let a: Animal;
let s: Size;

a = s; // Хорошо
s = a; // Хорошо

TypeScript только заботится о instance members. Он говорит: "Both have a feet property? Good enough for me!"

Приватные и защищенные члены

Однако, когда классы имеют private или protected члены,事情变得 stricter:

class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal {
constructor() { super("Rhino"); }
}

class Employee {
private name: string;
constructor(theName: string) { this.name = theName; }
}

let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");

animal = rhino; // Хорошо
animal = employee; // Ошибка: 'Animal' и 'Employee' несовместимы

Хотя Animal и Employee look the same, TypeScript treated them differently because their private члены come from different declarations.

Заключение

И вот мы здесь, мои кодировочные ученики! Мы отправились в путешествие по земле типсовместимости TypeScript. Помните, что TypeScript здесь, чтобы помочь вам писать лучшее, более безопасное код. Это как иметь friendly wizard, который смотрит вам через плечо и gently nudges вас, когда вы на грани ошибки.

Продолжайте практиковаться, продолжайте экспериментировать, и скоро вы будете cast TypeScript заклинания как профи! До下一次 встречи, счастливого кодирования!

Credits: Image by storyset