Làm cách nào để ngăn lỗi chữ ký Chỉ mục lỗi của loại đối tượng mặc nhiên có loại 'bất kỳ' nào khi biên dịch bản thảo với cờ noImplicitAny được bật?


309

Tôi luôn biên dịch typecript với cờ --noImplicitAny. Điều này có ý nghĩa khi tôi muốn kiểm tra kiểu của mình càng chặt chẽ càng tốt.

Vấn đề của tôi là với đoạn mã sau tôi gặp lỗi Index signature of object type implicitly has an 'any' type:

interface ISomeObject {
    firstKey:   string;
    secondKey:  string;
    thirdKey:   string;
}

let someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue'
};

let key: string = 'secondKey';

let secondValue: string = someObject[key];

Điều quan trọng cần lưu ý là ý tưởng là biến khóa xuất phát từ một nơi khác trong ứng dụng và có thể là bất kỳ khóa nào trong đối tượng.

Tôi đã cố gắng phân loại rõ ràng bằng cách:

let secondValue: string = <string>someObject[key];

Hoặc là kịch bản của tôi chỉ là không thể với --noImplicitAny?

Câu trả lời:


337

Thêm chữ ký chỉ mục sẽ cho TypeScript biết loại nên là gì.

Trong trường hợp của bạn đó sẽ là [key: string]: string;

interface ISomeObject {
    firstKey:      string;
    secondKey:     string;
    thirdKey:      string;
    [key: string]: string;
}

Tuy nhiên, điều này cũng bắt buộc tất cả các loại thuộc tính để khớp với chữ ký chỉ mục. Vì tất cả các thuộc tính là một stringnó hoạt động.

Mặc dù chữ ký chỉ mục là một cách mạnh mẽ để mô tả mảng và mẫu 'từ điển', nhưng chúng cũng thực thi rằng tất cả các thuộc tính khớp với kiểu trả về của chúng.

Biên tập:

Nếu các loại không khớp, loại liên kết có thể được sử dụng [key: string]: string|IOtherObject;

Với các loại kết hợp, sẽ tốt hơn nếu bạn để TypeScript suy ra loại thay vì xác định nó.

// Type of `secondValue` is `string|IOtherObject`
let secondValue = someObject[key];
// Type of `foo` is `string`
let foo = secondValue + '';

Mặc dù điều đó có thể hơi lộn xộn nếu bạn có nhiều loại khác nhau trong chữ ký chỉ mục. Thay thế cho điều đó là sử dụng anytrong chữ ký. [key: string]: any;Sau đó, bạn sẽ cần phải đúc các loại như bạn đã làm ở trên.


Và nếu giao diện của bạn trông giống giao diện ISomeObject {firstKey: string; secondKey: IOtherObject; } điều này không thể tôi đoán được?
Jasper Schulte

Cảm ơn! Sự kết hợp của bất kỳ loại nào cùng với việc đúc một loại cho mỗi trường hợp dường như là một cách bán.
Jasper Schulte

Xin chào, Làm thế nào để xử lý "anyObject [key: Object] ['name']"?
Code_Crash

hoặc nói điều gì đó như _obj = {}; let _dbKey = _props [key] ['name']; _obj [_dbKey] = this [khóa]; ở đây _props là đối tượng và đối tượng [key] cũng sẽ trả về một đối tượng sẽ có thuộc tính name.
Code_Crash

180

Một cách khác để tránh lỗi là sử dụng dàn diễn viên như thế này:

let secondValue: string = (<any>someObject)[key]; (Lưu ý dấu ngoặc đơn)

Vấn đề duy nhất là điều này không còn an toàn nữa, như bạn đang sử dụng any. Nhưng bạn luôn có thể quay trở lại đúng loại.

ps: Tôi đang sử dụng bản thảo 1.7, không chắc chắn về các phiên bản trước.


20
Để tránh cảnh báo tslint, bạn cũng có thể sử dụng:let secondValue: string = (someObject as any)[key];
briosheje

96

TypeScript 2.1 giới thiệu cách thức thanh lịch để xử lý vấn đề này.

const key: (keyof ISomeObject) = 'secondKey';
const secondValue: string = someObject[key];

Chúng ta có thể truy cập tất cả các tên thuộc tính đối tượng trong giai đoạn biên dịch theo keyoftừ khóa (xem changelog ).

Bạn chỉ cần thay thế stringloại biến bằng keyof ISomeObject. Bây giờ trình biên dịch biết keybiến được phép chỉ chứa tên thuộc tính ISomeObject.

Ví dụ đầy đủ:

interface ISomeObject {
    firstKey:   string;
    secondKey:  string;
    thirdKey:   number;
}

const someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   3
};

const key: (keyof ISomeObject) = 'secondKey';
const secondValue: string = someObject[key];

// You can mix types in interface, keyof will know which types you refer to.
const keyNumber: (keyof ISomeObject) = 'thirdKey';
const numberValue: number = someObject[keyNumber];

Mã trực tiếp trên typecriptang.org ( noImplicitAnytùy chọn thiết lập )

Đọc thêm với nhiều công keyofdụng hơn .


6
Tuy nhiên, nó sẽ không hoạt động nếu chúng tôi tuyên bố keyconst key = (keyof ISomeObject)= 'giây' + 'Khóa'
Thất vọng

55

Cài đặt tsconfig sau đây sẽ cho phép bạn bỏ qua các lỗi này - đặt thành đúng.

đàn ápImplicitAnyIndexErrors

Loại bỏ các lỗi noImplicitAny để lập chỉ mục các đối tượng thiếu chữ ký chỉ mục.


14
đó là điều bạn không nên làm - có lẽ ai đó trong nhóm của bạn đã thiết lập rõ ràng tùy chọn trình biên dịch này để làm cho mã chống đạn hơn!
atsu85

12
Tôi không đồng ý đây chính xác là những gì tùy chọn này được tạo ra: Cho phép ký hiệu dấu ngoặc với --noImplicitAny. Kết hợp hoàn hảo câu hỏi của op.
Ghetolay

4
Tôi đồng ý với @Ghetolay. Đây cũng là tùy chọn duy nhất nếu không thể sửa đổi giao diện. Ví dụ, với các giao diện nội bộ như XMLHttpRequest.
Marco Roy

1
Tôi cũng đồng ý với @Ghetolay. Tôi tò mò làm thế nào điều này khác biệt về chất với câu trả lời của Pedro Villa Verde (ngoài thực tế là mã ít xấu hơn). Tất cả chúng ta đều biết rằng nên truy cập vào một thuộc tính đối tượng bằng cách sử dụng một chuỗi, nhưng đôi khi chúng ta thích sự tự do đó trong khi hiểu được các rủi ro.
Stephen Paul

Đó chỉ là sự đánh đổi. Chọn những gì bạn thích: diện tích bề mặt ít lỗi hơn và truy cập chỉ mục nghiêm ngặt hoặc có nhiều diện tích bề mặt hơn cho các lỗi và dễ dàng truy cập các chỉ số không xác định. keyofToán tử TS2.1 có thể giúp giữ mọi thứ nghiêm ngặt, xem câu trả lời của Piotr!
trusktr

24

Giải pháp 'keyof' được đề cập ở trên hoạt động. Nhưng nếu biến chỉ được sử dụng một lần, ví dụ như lặp qua một đối tượng, v.v., bạn cũng có thể đánh máy nó.

for (const key in someObject) {
    sampleObject[key] = someObject[key as keyof ISomeObject];
}

Cảm ơn. Điều này hoạt động để truy cập khóa tùy ý khi lặp lại các khóa của đối tượng khác.
bucabay

19

sử dụng keyof typeof

const cat = {
    name: 'tuntun'
}

const key: string = 'name' 

cat[key as keyof typeof cat]

7

Tương tự như câu trả lời của @Piotr Lewandowski, nhưng trong một forEach:

const config: MyConfig = { ... };

Object.keys(config)
  .forEach((key: keyof MyConfig) => {
    if (config[key]) {
      // ...
    }
  });

Làm thế nào bạn có được điều này để làm việc? Tôi đang cố gắng điều tương tự (ts 3.8.3), mặc dù nó ném một lỗi nói rằng: Argument of type '(field: "id" | "url" | "name") => void' is not assignable to parameter of type '(value: string, index: number, array: string[]) => void'. Mã của tôi trông giống như vậy Object.keys(components).forEach((comp: Component) => {...}, đâu Componentlà một loại (như MyConfig).
theGirrafish

6

Khai báo đối tượng như thế này.

export interface Thread {
    id:number;
    messageIds: number[];
    participants: {
        [key:number]: number
    };
}

6

Không có chỉ mục? Sau đó làm cho riêng bạn!

Tôi đã định nghĩa toàn cầu đây là một cách dễ dàng để xác định chữ ký đối tượng. Tcó thể anynếu cần:

type Indexer<T> = { [ key: string ]: T };

Tôi chỉ thêm indexerlà một thành viên trong lớp.

indexer = this as unknown as Indexer<Fruit>;

Vì vậy, tôi kết thúc với điều này:

constructor(private breakpointResponsiveService: FeatureBoxBreakpointResponsiveService) {

}

apple: Fruit<string>;
pear: Fruit<string>;

// just a reference to 'this' at runtime
indexer = this as unknown as Indexer<Fruit>;

something() {

    this.indexer['apple'] = ...    // typed as Fruit

Lợi ích của việc này là bạn lấy lại được loại phù hợp - nhiều giải pháp sử dụng <any>sẽ làm mất việc gõ cho bạn. Hãy nhớ điều này không thực hiện bất kỳ xác minh thời gian chạy. Bạn vẫn sẽ cần kiểm tra nếu có thứ gì đó tồn tại nếu bạn không biết chắc chắn nó tồn tại.

Nếu bạn muốn thận trọng quá mức và bạn đang sử dụng, strictbạn có thể làm điều này để tiết lộ tất cả các địa điểm bạn có thể cần thực hiện kiểm tra không xác định rõ ràng:

type OptionalIndexed<T> = { [ key: string ]: T | undefined };

Tôi thường không thấy điều này là cần thiết vì nếu tôi có một thuộc tính chuỗi từ một nơi nào đó tôi thường biết rằng nó hợp lệ.

Tôi đã tìm thấy phương pháp này đặc biệt hữu ích nếu tôi có nhiều mã cần truy cập vào bộ chỉ mục và việc gõ có thể được thay đổi chỉ ở một nơi.

Lưu ý: Tôi đang sử dụng strictchế độ, và điều unknownnày chắc chắn là cần thiết.

Mã được biên dịch sẽ giống indexer = thisnhư vậy, vì vậy nó rất giống với khi bản thảo tạo _this = thischo bạn.


1
Một số trường hợp bạn có thể sử dụng Record<T>loại thay thế - hiện tại tôi không thể nghiên cứu chi tiết tốt về điều này nhưng đối với một số trường hợp hạn chế, nó có thể hoạt động tốt hơn.
Simon_Weaver

5

Tạo giao diện để xác định giao diện 'bộ chỉ mục'

Sau đó tạo đối tượng của bạn với chỉ mục đó.

Lưu ý: điều này vẫn sẽ có các vấn đề tương tự mà các câu trả lời khác đã mô tả liên quan đến việc thực thi loại của từng mặt hàng - nhưng đó thường chính xác là những gì bạn muốn.

Bạn có thể tạo tham số loại chung bất cứ điều gì bạn cần: ObjectIndexer< Dog | Cat>

// this should be global somewhere, or you may already be 
// using a library that provides such a type
export interface ObjectIndexer<T> {
  [id: string]: T;
}

interface ISomeObject extends ObjectIndexer<string>
{
    firstKey:   string;
    secondKey:  string;
    thirdKey:   string;
}

let someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue'
};

let key: string = 'secondKey';

let secondValue: string = someObject[key];

Sân chơi đánh máy


Bạn thậm chí có thể sử dụng điều này trong một ràng buộc chung khi xác định một loại chung:

export class SmartFormGroup<T extends IndexableObject<any>> extends FormGroup

Sau đó, Tbên trong lớp có thể được lập chỉ mục :-)


Tôi không nghĩ rằng có một giao diện 'tích hợp' tiêu chuẩn cho Dictionaryđại diện đó { [key: string]: T }, nhưng nếu có, xin vui lòng chỉnh sửa câu hỏi này để loại bỏ câu hỏi của tôi ObjectIndexer.
Simon_Weaver

3

Khai báo loại mà khóa của nó là chuỗi và giá trị có thể là bất kỳ sau đó khai báo đối tượng với loại này và lint sẽ không hiển thị

type MyType = {[key: string]: any};

Vì vậy, mã của bạn sẽ là

type ISomeType = {[key: string]: any};

    let someObject: ISomeType = {
        firstKey:   'firstValue',
        secondKey:  'secondValue',
        thirdKey:   'thirdValue'
    };

    let key: string = 'secondKey';

    let secondValue: string = someObject[key];

1

Ngày nay giải pháp tốt hơn là khai báo các loại. Giống

enum SomeObjectKeys {
    firstKey = 'firstKey',
    secondKey = 'secondKey',
    thirdKey = 'thirdKey',
}

let someObject: Record<SomeObjectKeys, string> = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue',
};

let key: SomeObjectKeys = 'secondKey';

let secondValue: string = someObject[key];

1

Giải pháp đơn giản nhất mà tôi có thể tìm thấy bằng cách sử dụng Bản mô tả 3.1 trong 3 bước là:

1) Tạo giao diện

interface IOriginal {
    original: { [key: string]: any }
}

2) Tạo một bản sao đánh máy

let copy: IOriginal = (original as any)[key];

3) Sử dụng ở bất cứ đâu (bao gồm cả JSX)

<input customProp={copy} />
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.