Làm thế nào để thực hiện một trang trí bản thảo?


207

TypeScript 1.5 hiện có trang trí .

Ai đó có thể cung cấp một ví dụ đơn giản thể hiện cách thức phù hợp để thực hiện một trình trang trí và mô tả các đối số trong chữ ký trang trí hợp lệ có thể có nghĩa là gì?

declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;

Ngoài ra, có bất kỳ cân nhắc thực hành tốt nhất nên được ghi nhớ trong khi thực hiện một trang trí?


Lưu ý với bản thân tôi :-) nếu bạn muốn tiêm thuốc @Injectablevào trang trí, hãy tham khảo
Anand Rockzz 27/12/18

Tôi sẽ đề nghị xem xét nhiều ví dụ mà dự án này có. Có nhiều trang trí - một số rất đơn giản và một số có thể khó hiểu hơn một chút: github.com/vlio20/utils-decorators
vlio20

Câu trả lời:


396

Cuối cùng tôi đã chơi xung quanh với các nhà trang trí và quyết định ghi lại những gì tôi đã tìm ra cho bất cứ ai muốn tận dụng điều này trước khi bất kỳ tài liệu nào được đưa ra. Xin vui lòng chỉnh sửa này nếu bạn thấy bất kỳ sai lầm.

Điểm chung

  • Trình trang trí được gọi khi lớp được khai báo không phải khi một đối tượng được khởi tạo.
  • Nhiều trang trí có thể được định nghĩa trên cùng một Class / Thuộc tính / Phương thức / Tham số.
  • Trang trí không được phép trên các nhà xây dựng.

Một trang trí hợp lệ nên là:

  1. Chỉ định cho một trong các loại Trang trí ( ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator).
  2. Trả về một giá trị (trong trường hợp các trình trang trí lớp và trình trang trí phương thức) có thể gán cho giá trị được trang trí.

Tài liệu tham khảo


Phương pháp / Trang trí Accessor chính thức

Thông số thực hiện:

  • target: Nguyên mẫu của lớp ( Object).
  • propertyKey: Tên của phương thức ( string| symbol).
  • descriptor: A TypedPropertyDescriptor- Nếu bạn không quen với các khóa của bộ mô tả, tôi khuyên bạn nên đọc về nó trong tài liệu này trên Object.defineProperty(đó là tham số thứ ba).

Ví dụ - Không có đối số

Sử dụng:

class MyClass {
    @log
    myMethod(arg: string) { 
        return "Message -- " + arg;
    }
}

Thực hiện:

function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
    const originalMethod = descriptor.value; // save a reference to the original method

    // NOTE: Do not use arrow syntax here. Use a function expression in 
    // order to use the correct value of `this` in this method (see notes below)
    descriptor.value = function(...args: any[]) {
        // pre
        console.log("The method args are: " + JSON.stringify(args));
        // run and store result
        const result = originalMethod.apply(this, args);
        // post
        console.log("The return value is: " + result);
        // return the result of the original method (or modify it before returning)
        return result;
    };

    return descriptor;
}

Đầu vào:

new MyClass().myMethod("testing");

Đầu ra:

Phương thức lập luận là: ["tests"]

Giá trị trả về là: Tin nhắn - kiểm tra

Ghi chú:

  • Không sử dụng cú pháp mũi tên khi đặt giá trị của bộ mô tả. Bối cảnh thissẽ không là ví dụ nếu bạn làm.
  • Tốt hơn là sửa đổi bộ mô tả gốc hơn là ghi đè lên bộ mô tả hiện tại bằng cách trả về một bộ mô tả mới. Điều này cho phép bạn sử dụng nhiều trình trang trí chỉnh sửa mô tả mà không ghi đè lên những gì một trình trang trí khác đã làm. Làm điều này cho phép bạn sử dụng một cái gì đó giống @enumerable(false)@logcùng một lúc (Ví dụ: Xấu so với Tốt )
  • Hữu ích : Đối số loại TypedPropertyDescriptorcó thể được sử dụng để hạn chế chữ ký phương thức nào ( Ví dụ phương thức ) hoặc chữ ký của người truy cập ( Ví dụ về trình truy cập ) mà trình trang trí có thể được đưa vào.

Ví dụ - Với đối số (Nhà máy trang trí)

Khi sử dụng đối số, bạn phải khai báo một hàm với các tham số của trình trang trí, sau đó trả về một hàm có chữ ký của ví dụ mà không có đối số.

class MyClass {
    @enumerable(false)
    get prop() {
        return true;
    }
}

function enumerable(isEnumerable: boolean) {
    return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => {
        descriptor.enumerable = isEnumerable;
        return descriptor;
    };
}

Trang trí phương pháp tĩnh

Tương tự như một trình trang trí phương thức với một số khác biệt:

  • targetTham số của nó là hàm xây dựng chính nó chứ không phải nguyên mẫu.
  • Bộ mô tả được xác định trên hàm xây dựng chứ không phải nguyên mẫu.

Lớp trang trí

@isTestable
class MyClass {}

Thông số thực hiện:

  • target: Lớp trang trí được khai báo trên ( TFunction extends Function).

Ví dụ sử dụng : Sử dụng api siêu dữ liệu để lưu trữ thông tin trên một lớp.


Trang trí tài sản

class MyClass {
    @serialize
    name: string;
}

Thông số thực hiện:

  • target: Nguyên mẫu của lớp ( Object).
  • propertyKey: Tên của tài sản ( string| symbol).

Ví dụ sử dụng : Tạo một trình @serialize("serializedName")trang trí và thêm tên thuộc tính vào danh sách các thuộc tính để tuần tự hóa.


Trang trí tham số

class MyClass {
    myMethod(@myDecorator myParameter: string) {}
}

Thông số thực hiện:

  • target: Nguyên mẫu của lớp ( Functiondường như Functionkhông còn hoạt động nữa. Bạn nên sử dụng anyhoặc Objectở đây ngay bây giờ để sử dụng trình trang trí trong bất kỳ lớp nào. Hoặc chỉ định (các) loại lớp bạn muốn hạn chế)
  • propertyKey: Tên của phương thức ( string| symbol).
  • parameterIndex: Chỉ mục của tham số trong danh sách các tham số của hàm ( number).

Ví dụ đơn giản

Ví dụ chi tiết


Bạn có biết nơi để tìm một ví dụ trang trí tham số? Tôi đã cố gắng thực hiện một cái mà không thành công github.com/Microsoft/TypeScript/issues/iêu
Remo H. Jansen

1
@OweRReLoaDeD Tôi đã thêm một ví dụ dưới trang trí tham số chỉ ghi lại những gì được truyền cho trình trang trí. Tôi không chắc chắn nếu điều đó hữu ích mặc dù. Tôi không thể nghĩ ra một ví dụ tốt vào lúc này.
David Sherret

FYI Tôi đã thu thập và điều chỉnh thông tin này trên github: github.com/arolson101/typescript-decorators
arolson101 15/08/2015

Cờ --experimentalDecorators phải được đặt để ví dụ này hoạt động
Trident D'Gao

Tôi hơi bối rối về những gì targethoặc prototype of the classkeyđề cập đến, ai đó có thể vui lòng giải thích về điều đó?
Satej S

8

Một điều quan trọng tôi không thấy trong các câu trả lời khác:

Nhà máy trang trí

Nếu chúng ta muốn tùy chỉnh cách áp dụng trang trí cho khai báo, chúng ta có thể viết một nhà máy trang trí. Một xưởng trang trí đơn giản là một hàm trả về biểu thức sẽ được gọi bởi trình trang trí trong thời gian chạy.

// This is a factory, returns one of ClassDecorator,
// PropertyDecorator, MethodDecorator, ParameterDecorator
function Entity(discriminator: string):  {
    return function(target) {
        // this is the decorator, in this case ClassDecorator.
    }
}

@Entity("cust")
export class MyCustomer { ... }

Kiểm tra chương Sổ tay trang trí TypeScript .


4
class Foo {
  @consoleLogger 
  Boo(name:string) { return "Hello, " + name }
}
  • đích: nguyên mẫu của lớp trong trường hợp trên đó là "Foo"
  • propertyKey: tên của phương thức được gọi, trong trường hợp trên "Boo"
  • mô tả: mô tả đối tượng => chứa thuộc tính giá trị, lần lượt là chính hàm: function (name) {return 'Hello' + name; }

Bạn có thể thực hiện một cái gì đó ghi nhật ký từng cuộc gọi vào bàn điều khiển:

function consoleLogger(target: Function, key:string, value:any) 
{
  return value: (...args: any[]) => 
  {
     var a = args.map(a => JSON.stringify(a)).join();
     var result = value.value.apply(this, args);
     var r = JSON.stringify(result);

     console.log('called method' + key + ' with args ' + a + ' returned result ' + r);

     return result;
  }     
}

1
Đây là một nhiệm vụ khó khăn để biên dịch với các cài đặt trình biên dịch nghiêm ngặt
PandaWood

Trên thực tế, điều này là sai và không thể biên dịch, cần phải niềng răng trực tiếp sau khi trả về {value: ...}. Điều này thậm chí có thể được nhìn thấy từ một nguồn tiềm năng của mã của bạn - blog.wolksoftware.com/ Từ
PandaWood
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.