【问题标题】:Can't extends Generic for TypeScript class无法为 TypeScript 类扩展泛型
【发布时间】:2023-01-12 20:34:09
【问题描述】:

我无法理解 TypeScript Generic with classes 的这种行为。

打字稿

interface IProvider<K extends {[key: string]: any}> {
  data: K;
}


class Provider<T extends {[key: string]: any}> implements IProvider<T> {
  data: T;
  
  constructor(arg?: T) {
    this.data = arg || {}; // This is not allowed.
  }
}


type User = {
  [key: string]: any
}

const x = new Provider<User>();

错误是:

Type 'T | {}' is not assignable to type 'T'.  
'T | {}' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ [key: string]: any; }'.  
Type '{}' is not assignable to type 'T'.
      '{}' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ [key: string]: any; }'.

但是,如果我删除可选运算符,它就可以正常工作。

打字稿

class Provider<T extends {[key: string]: any}> implements IProvider<T> {
  data: T;
  
  constructor(arg: T) { // no optional 
    this.data = arg || {}; // Now it works.
  }
}

请帮我解释一下。非常感谢你!

【问题讨论】:

    标签: typescript class generics constraints


    【解决方案1】:

    该错误正确地警告您潜在的不健全。

    考虑以下场景,其中 User 类型具有 string 类型的属性 a。当arg 是可选的时,我们不必将任何对象传递给将使用{} 初始化数据的构造函数。

    访问 x.data.a 将导致运行时值为 undefined,即使我们将其键入为 string

    type User = {
      a: string
    }
    
    const x = new Provider<User>();
    
    x.data.a.charCodeAt(0) // runtime Error!
    

    如果我们强制构造函数参数,则不会发生这种情况。


    Playground

    【讨论】:

    • @Tobais S.,谢谢您的回答。有没有办法解决这个问题?如果缺少 arg,如何为 data 设置默认值?
    • 这实际上取决于您希望如何处理不健全的问题。我们可以将 data 包装成 Partial 表示任何属性可能尝试访问它们时不存在:tsplay.dev/wRzg1w
    【解决方案2】:

    今天是你的幸运日。

    我不想谈论它为什么不起作用,而是解释泛型的概念、原因以及我们应该在哪里使用它。

    1-行为

    例如,我有 3 个对象,

    • 产品
    • 用户
    • 联系方式

    我有一个 Printer 类,它可以打印任何实现 Printable 接口的对象。

    export interface Printable {
      print(): string;
    }
    
    export interface Printer<T extends Printable> {
      print(obj: T): string;
    }
    
    export class BlackWhitePrinter<T extends Printable> {
      print(obj: T) {
        return `[BlackWhitePrinter] ` + obj.print();
      }
    }
    
    export class ColorPrinter<T extends Printable> {
      print(obj: T) {
        return `[Color Printer] ` + obj.print();
      }
    }
    
    export class Product implements Printable {
      readonly name: string = 'product name';
      print() {
        return this.name;
      }
    }
    
    export class User implements Printable {
      readonly username: string = 'username';
      print() {
        return this.username;
      }
    }
    
    export class Contact implements Printable {
      readonly phone: string = '+1 999 999 99 99';
      print() {
        return this.phone;
      }
    }
    
    const blackWhitePrinter = new BlackWhitePrinter();
    const colorPrinter = new BlackWhitePrinter();
    
    blackWhitePrinter.print(new User());
    blackWhitePrinter.print(new Product());
    blackWhitePrinter.print(new Contact());
    
    colorPrinter.print(new User());
    colorPrinter.print(new Product());
    colorPrinter.print(new Contact());
    

    2- 数据和行为

    interface PhoneNumber {
      phoneNumber?: string;
    }
    
    interface EmailAddress {
      email?: string;
    }
    
    interface CanCall {
      call(contact: PhoneNumber): void;
    }
    
    interface CanEmail {
      email(contact: EmailAddress): void;
    }
    
    interface Contact extends PhoneNumber, EmailAddress {}
    
    interface ContactWithAddress extends Contact {
      address?: string;
    }
    
    /**
     * Android phone can call and send email
     */
    export class AndroidPhone<T extends ContactWithAddress>
      implements CanCall, CanEmail
    {
      constructor(public readonly contacts: T[]) {}
    
      call(contact: PhoneNumber): void {
        console.log(`Call to ${contact.phoneNumber}`);
      }
      email(contact: EmailAddress): void {
        console.log(`Email to ${contact.email}`);
      }
    }
    
    /**
     * Regular phone can call only
     */
    export class RegularPhone<T extends PhoneNumber> implements CanCall {
      constructor(public readonly contacts: T[]) {}
      call(contact: PhoneNumber): void {
        console.log(`Calling to ${contact.phoneNumber}`);
      }
    }
    
    /**
     * Unfortunately, some people only have regular phones.
     */
    class PoorUser {
      constructor(public readonly phone: CanCall) {}
    }
    
    /**
     * Some dudes, always use the last vertion of XYZ Smart phones
     */
    class RichUser<T extends CanCall & CanEmail> {
      constructor(public readonly phone: T) {}
    }
    
    const poorUser = new PoorUser(
      new RegularPhone([{ phoneNumber: '+1 999 999 99 99' }])
    );
    
    /**
     * Even after we give a smart phone to poor people, they cannot send emails because they do not have internet connection :(
     */
    const poorUser1 = new PoorUser(
      new AndroidPhone([{ phoneNumber: '+1 999 999 99 99' }])
    );
    
    /**
     * Hopefully, they can call if they paid the bill.
     */
    poorUser.phone.call({ phoneNumber: '+1 999 999 99 99' });
    // poorUser1.phone.email({email:'.......'}) // Cannot send email because he is not aware of the future!
    
    /**
     * Rich people neither call nor email others because they are always busy and they never die.
     */
    const richUser = new RichUser(
      new AndroidPhone([
        { email: 'email@email.com', phoneNumber: '+1 999 999 99 99' },
      ])
    );
    
    /**
     * Another rich call.
     */
    richUser.phone.call({ phoneNumber: '+1 999 999 99 99' });
    
    /**
     * Another rich email. If you are getting lots of emails, it means you are 
     * poor because rich people do not open their emails, their employees do.
     * I've never seen any rich googling or searching in StackOverflow "How to replace Money with Gold?", probably they search things like that. Did you see any?
     */
    richUser.phone.email({ email: 'email@email.com' });
    

    【讨论】:

      猜你喜欢
      • 2020-09-12
      • 1970-01-01
      • 2021-03-25
      • 2015-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多