Làm thế nào để bạn thiết lập rõ ràng một thuộc tính mới trên `window` trong TypeScript?


621

Tôi thiết lập không gian tên toàn cầu cho các đối tượng của mình bằng cách đặt rõ ràng một thuộc tính window.

window.MyNamespace = window.MyNamespace || {};

TypeScript gạch chân MyNamespacevà phàn nàn rằng:

Thuộc tính 'MyNamespace' không tồn tại trên giá trị của loại 'cửa sổ' bất kỳ "

Tôi có thể làm cho mã hoạt động bằng cách khai báo MyNamespacenhư một biến xung quanh và loại bỏ nhân chứng windownhưng tôi không muốn làm điều đó.

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

Làm thế nào tôi có thể giữ windowtrong đó và làm cho TypeScript hạnh phúc?

Là một lưu ý phụ, tôi thấy thật buồn cười khi TypeScript phàn nàn vì nó nói với tôi rằng đó windowlà loại anymà chắc chắn có thể chứa bất cứ thứ gì.


Câu trả lời:


692

Chỉ cần tìm câu trả lời cho câu hỏi này trong câu trả lời khác của StackOverflow .

declare global {
    interface Window { MyNamespace: any; }
}

window.MyNamespace = window.MyNamespace || {};

Về cơ bản, bạn cần mở rộng windowgiao diện hiện có để nói với nó về tài sản mới của bạn.


12
Lưu ý viết hoa W trong Window. Điều đó làm tôi vấp ngã.
ajm

2
Tôi không thể lấy cái này để biên dịch với tsc 1.0.1.0. Câu trả lời của Blake Mitchell đã làm việc cho tôi, mặc dù.
Pat

43
Xin lưu ý rằng điều này chỉ hoạt động khi được khai báo trong một .d.tstệp riêng biệt . Nó không hoạt động khi được sử dụng trong một .tstệp sử dụng nhập và xuất chính nó. Xem câu trả lời này .
cdauth

36
Các declare global { interface Window { ... } }hoạt động với TypeScript 2.5.2, không cần .d.tstệp như đã đề cập ở trên
tanguy_k

2
Lúc đầu bản thảo nói với tôi : error TS1234: An ambient module declaration is only allowed at the top level in a file., khi tôi đặt nó ở đầu tập tin, nó đã sửa lỗi đó, vì vậy bạn cũng có thể phải làm điều đó.
Zelphir Kaltstahl

397

Để giữ cho nó năng động, chỉ cần sử dụng:

(<any>window).MyNamespace

9
Bất kỳ ý tưởng làm thế nào để làm điều này trong một tập tin tsx?
Velop

2
@martin: <any> làm cho nó rõ ràng, vì vậy nó hoạt động tốt, tôi tin. Khác: Nếu bạn đang sử dụng Bản mô tả với React hoặc môi trường JSX khác (dẫn đến cú pháp TSX), bạn sẽ phải sử dụng asthay vì <>. Xem câu trả lời của @ david-boyd bên dưới .
Don

31
Tôi đã phải sử dụng (cửa sổ như bất kỳ) .MyNamespace
Arsalan Ahmad

Tương tự cho this-return (<any> this)['something in scope']
HankCa

1
Tôi đã phải vô hiệu hóa tslint trên dòng đó với/* tslint:disable */
Michael Yagudaev

197

NHƯ CÁC LOẠI HÌNH ^ 3.4.3 GIẢI PHÁP NÀY KHÔNG CÓ CÔNG TRÌNH NÀO

Hoặc là...

bạn chỉ có thể gõ:

window['MyNamespace']

và bạn sẽ không gặp lỗi biên dịch và nó hoạt động giống như gõ window.MyNamespace


15
nhưng bạn có thể sẽ gặp lỗi tslint ... Nếu bạn có một khóa học
smnbbrv

69
Điều này hoàn toàn bay bổng khi đối mặt với việc gõ mạnh, toàn bộ ý tưởng đằng sau TypeScript.
d512

18
@ user1334007 cũng sử dụng toàn cầu. Tuy nhiên, một số mã kế thừa yêu cầu nó.
nathancahill

3
@Ramesh window['MyNamespace']()(chỉ cần thêm dấu ngoặc)
iBaff

6
"Điều này hoàn toàn bay bổng khi đối mặt với việc gõ mạnh, toàn bộ ý tưởng đằng sau TypeScript.". TỐT!
Cody

188

Sử dụng TSX? Không có câu trả lời nào khác làm việc cho tôi.

Đây là những gì tôi đã làm:

(window as any).MyNamespace

4
Giống như (<any> window).MyNamespacethực tế
Dmitry Parzhitsky

44
Điều này cũng tương tự ngoại trừ khi sử dụng TSX, bởi vì <any>được hiểu là JSX, không phải là kiểu truyền.
Jake Boone

1
Nhờ
Jignesh Raval

Cửa sổ nào? Vậy thì tại sao bạn lại sử dụng bản thảo? Chỉ cần sử dụng Javascript x)
MarcoLe

71

Câu trả lời được chấp nhận là những gì tôi từng sử dụng, nhưng với TypeScript 0.9. * Nó không còn hoạt động. Định nghĩa mới của Windowgiao diện dường như thay thế hoàn toàn định nghĩa tích hợp, thay vì tăng nó.

Tôi đã thực hiện để làm điều này thay vào đó:

interface MyWindow extends Window {
    myFunction(): void;
}

declare var window: MyWindow;

CẬP NHẬT: Với TypeScript 0.9.5, câu trả lời được chấp nhận sẽ hoạt động trở lại.


2
Điều này cũng hoạt động với các mô-đun được sử dụng bởi TypeScript 2.0.8. Ví dụ: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() Sau đó, bạn có thể gọi foo () ví dụ: từ bảng điều khiển Chrome Dev Tools như mc.foo()
Martin Majewski

Đây là một câu trả lời rất hay nếu bạn không muốn tuyên bố một cái gì đó là toàn cầu. Mặt khác, bạn cần gọi declare var...trong mọi tập tin bạn cần.
Puce

Thêm một cho cách tiếp cận này vì trong trường hợp này, bạn không xung đột với các gói khác mở rộng cửa sổ toàn cầu trong monorepo.
Dmitrii Sorin

Cảm ơn bạn! Câu trả lời hay nhất IMO. Người ta không nên ghi đè Windowcho thực tế đơn giản rằng nó không phải là một cửa sổ vanilla. Chỉ cần lách luật kiểm tra hoặc cố gắng đánh lừa TS cũng không phải là cách để làm điều đó.
Rutger Willems

Điều này không hoạt động kể từ TS 3.6, thật không may
Jacob Soderlund

65

Toàn cầu là "ác" :), tôi nghĩ cách tốt nhất để có tính di động là:

Trước tiên, bạn xuất giao diện: (ví dụ: ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

Thứ hai bạn nhập

import {CustomWindow} from './custom.window.ts';

Cửa sổ var toàn cầu cast thứ ba với CustomWindow

declare let window: CustomWindow;

Theo cách này, bạn cũng không có dòng màu đỏ trong IDE khác nếu bạn sử dụng với các thuộc tính tồn tại của đối tượng cửa sổ, vì vậy cuối cùng hãy thử:

window.customAttribute = 'works';
window.location.href = '/works';

Đã thử nghiệm với Bản mô tả 2.4.x và mới nhất!


1
bạn có thể muốn mở rộng lý do tại sao toàn cầu là xấu xa. Trong trường hợp của chúng tôi, chúng tôi đang sử dụng API không được tiêu chuẩn hóa trên đối tượng cửa sổ. Nó là polyfill cho bây giờ. Đó thực sự là xấu xa?
Mathijs Segers

1
@MathijsSegers tôi không biết đặc biệt là trường hợp của bạn nhưng .. về toàn cầu là xấu không chỉ là về javascript .. có trong mọi ngôn ngữ .. một số lý do là: - Bạn không thể áp dụng mô hình thiết kế tốt - Vấn đề về bộ nhớ và hiệu suất (Bạn Có chúng ở khắp mọi nơi, hãy thử đính kèm một cái gì đó vào String Object và xem điều gì sẽ xảy ra khi bạn tạo String mới) - Xung đột tên gây ra hiệu ứng phụ trên mã .. (đặc biệt là trên javascript có tính chất không đồng bộ) Tôi có thể tiếp tục nhưng tôi nghĩ bạn có thể hình ảnh phần còn lại ...
onalbi

1
@onabi Tôi thực sự đang nói về một tính năng của trình duyệt. Nó có sẵn trên toàn cầu vì nó là trình duyệt. Bạn thực sự sẽ đề nghị nó vẫn còn sai để đi cho định nghĩa toàn cầu? Ý tôi là chúng tôi có thể triển khai Ponyfills nhưng điều này sẽ làm mờ các trình duyệt thực sự hỗ trợ tính năng này (ví dụ như api toàn màn hình). Điều đó nói rằng tôi không tin rằng tất cả các thế giới là ác quỷ.
Mathijs Segers

2
@Mathijs Theo tôi, biến toàn cục nên tránh sử dụng hoặc sửa đổi ở nơi chúng ta có thể .. đúng là cửa sổ có sẵn kể từ khi trình duyệt tồn tại nhưng cũng đúng là đã bị đột biến .. vì vậy, ví dụ như bây giờ chúng ta xác định window.feature = 'Tính năng'; và điều này được sử dụng theo cách mã hóa lớn .. điều gì xảy ra nếu window.feature được thêm bởi các trình duyệt trên tất cả các mã mà tính năng này bị ghi đè .. dù sao tôi đã đưa ra một lời giải thích về câu của tôi là không đi ngược lại với bạn .. liên quan ...
onalbi


43

Đối với những người sử dụng CLI góc, điều này rất đơn giản:

src / polyfills.ts

declare global {
  interface Window {
    myCustomFn: () => void;
  }
}

my-custom-utils.ts

window.myCustomFn = function () {
  ...
};

Nếu bạn đang sử dụng IntelliJ, bạn cũng cần thay đổi cài đặt sau trong IDE trước khi nhận các polyfill mới của bạn:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check 'Use TypeScript Service'.

1
declare globallà mẹo và câu trả lời này không thực sự cụ thể đối với Angular CLI ...
Avindra Goolcharan

31

Tôi không cần phải làm điều này rất thường xuyên, trường hợp duy nhất tôi gặp phải là khi sử dụng Redux Devtools với phần mềm trung gian.

Tôi chỉ đơn giản là đã làm:

const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

Hoặc bạn có thể làm:

let myWindow = window as any;

và sau đó myWindow.myProp = 'my value';


1
Trong trường hợp này, bạn cũng có thể cài đặt gói npm, chứa tất cả các định nghĩa - như được chỉ định trong tài liệu
bbrinx

Bạn có thể làm điều này, nhưng đó không phải là câu trả lời cho câu hỏi, mà là một cách giải quyết
Vitalij

Nhờ vậy, tôi có thể cho phép các công cụ Redux đúng theo nguyên cảo bằng cách sử dụng (cửa sổ như bất kỳ) .__ REDUX_DEVTOOLS_EXTENSION__ && (cửa sổ như bất kỳ) .__ REDUX_DEVTOOLS_EXTENSION __ ())
danivicario

20

Hầu hết các câu trả lời khác là không hoàn hảo.

  • Một số trong số họ chỉ đàn áp các loại suy luận cho cửa hàng.
  • Một số khác chỉ quan tâm đến biến toàn cục là không gian tên, nhưng không phải là giao diện / lớp

Tôi cũng gặp phải vấn đề tương tự sáng nay. Tôi đã thử rất nhiều "giải pháp" trên SO, nhưng không có giải pháp nào tạo ra lỗi hoàn toàn và cho phép kích hoạt kiểu nhảy trong IDE (webstorm hoặc vscode).

Cuối cùng, từ đây

https://github.com/Microsoft/TypeScript/issues/3180#issuecomment-102523512

, Tôi tìm thấy một giải pháp hợp lý để đính kèm các kiểu chữ cho biến toàn cục, hoạt động như cả giao diện / lớp và không gian tên cả .

Ví dụ dưới đây:

// typings.d.ts
declare interface Window {
    myNamespace?: MyNamespace & typeof MyNamespace
}

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

Bây giờ, đoạn mã trên hợp nhất các kiểu chữ của không gian tên MyNamespacevà giao diện MyNamespacevào biến toàn cục myNamespace(thuộc tính của cửa sổ).


2
Cảm ơn - đây là người duy nhất bạn dường như có thể sử dụng trong bối cảnh không phải mô-đun xung quanh.
Tyler Sebastian


13

Đây là cách thực hiện, nếu bạn đang sử dụng Trình quản lý định nghĩa TypeScript !

npm install typings --global

Tạo typings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

Cài đặt cách gõ tùy chỉnh của bạn:

typings install file:typings/custom/window.d.ts --save --global

Xong, sử dụng nó‌ ! Bản đánh máy sẽ không còn phàn nàn nữa:

window.MyNamespace = window.MyNamespace || {};

1
FWIW typingsđã bị lỗi thời với Bản mô tả 2.0 (giữa năm 2016) và đã được chủ sở hữu lưu trữ.
mrm

13

Typscript không thực hiện typecheck trên các thuộc tính chuỗi.

window["newProperty"] = customObj;

Tốt nhất, nên tránh kịch bản biến toàn cục. Đôi khi tôi sử dụng nó để gỡ lỗi một đối tượng trong bảng điều khiển trình duyệt.



8

Nếu bạn đang sử dụng Bản mô tả 3.x, bạn có thể bỏ qua declare globalphần trong các câu trả lời khác và thay vào đó chỉ sử dụng:

interface Window {
  someValue: string
  another: boolean
}

Điều này làm việc với tôi khi sử dụng Typecript 3.3, WebPack và TSLint.


Không hoạt động trong ^3.4.3. Câu trả lời này có hiệu quả với tôi stackoverflow.com/a/42841166/1114926
Green

7

Để tham khảo (đây là câu trả lời đúng):

Bên trong một .d.tstệp định nghĩa

type MyGlobalFunctionType = (name: string) => void

Nếu bạn làm việc trong trình duyệt, bạn thêm thành viên vào ngữ cảnh cửa sổ của trình duyệt bằng cách mở lại giao diện của Window:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

Ý tưởng tương tự cho NodeJS:

declare module NodeJS {
  interface Global {
    myGlobalFunction: MyGlobalFunctionType
  }
}

Bây giờ bạn khai báo biến gốc (thực sự sẽ sống trên cửa sổ hoặc toàn cầu)

declare const myGlobalFunction: MyGlobalFunctionType;

Sau đó, trong một .tstệp thông thường , nhưng được nhập dưới dạng tác dụng phụ, bạn thực sự triển khai nó:

global/* or window */.myGlobalFunction = function (name: string) {
  console.log("Hey !", name);
};

Và cuối cùng sử dụng nó ở nơi khác trong cơ sở mã, với một trong hai:

global/* or window */.myGlobalFunction("Kevin");

myGlobalFunction("Kevin");

Cảm ơn, đây là ví dụ tốt nhất mà tôi tìm thấy và hoạt động tốt.
Nick G.

6

Tạo giao diện tùy chỉnh mở rộng Cửa sổ và thêm thuộc tính tùy chỉnh của bạn làm tùy chọn.

Sau đó, hãy để customWindow sử dụng giao diện tùy chỉnh, nhưng có giá trị với cửa sổ gốc.

Nó hoạt động với typecript@3.1.3.

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {} 

5

Đối với những người muốn đặt thuộc tính động hoặc tính toán trên windowđối tượng, bạn sẽ thấy điều đó là không thể với declare globalphương thức. Để làm rõ cho trường hợp sử dụng này

window[DynamicObject.key] // Element implicitly has an 'any' type because type Window has no index signature

Bạn có thể cố gắng làm một cái gì đó như thế này

declare global {
  interface Window {
    [DyanmicObject.key]: string; // error RIP
  }
}

Ở trên sẽ lỗi mặc dù. Điều này là do trong Bản mô tả, các giao diện không chơi tốt với các thuộc tính được tính toán và sẽ gây ra lỗi như

A computed property name in an interface must directly refer to a built-in symbol

Để giải quyết vấn đề này, bạn có thể đi với đề xuất đúc windowđể <any>bạn có thể làm

(window as any)[DynamicObject.key]

3
Đây là năm 2019.
Qwerty

4

Sử dụng tạo-Reac-app v3.3 Tôi thấy cách dễ nhất để đạt được điều này là mở rộng Windowloại trong tự động tạo react-app-env.d.ts:

interface Window {
    MyNamespace: any;
}

3

Đầu tiên bạn cần khai báo đối tượng cửa sổ trong phạm vi hiện tại.
Bởi vì bản thảo muốn biết loại của đối tượng.
Vì đối tượng cửa sổ được định nghĩa ở một nơi khác nên bạn không thể xác định lại nó.
Nhưng bạn có thể khai báo như sau: -

declare var window: any;

Điều này sẽ không xác định lại đối tượng cửa sổ hoặc nó sẽ không tạo ra một biến khác có tên window.
Điều này có nghĩa là cửa sổ được xác định ở một nơi khác và bạn chỉ tham chiếu nó trong phạm vi hiện tại.

Sau đó, bạn có thể tham khảo đối tượng MyNamespace của mình chỉ bằng cách: -

window.MyNamespace

Hoặc bạn có thể đặt thuộc tính mới trên windowđối tượng chỉ bằng cách: -

window.MyNamespace = MyObject

Và bây giờ bản thảo sẽ không phàn nàn.


Tôi có thể biết trường hợp sử dụng của bạn muốn biết nơi windowđược xác định?
Mav55

2

Tôi muốn sử dụng nó trong thư viện Angular (6) ngày hôm nay và tôi phải mất một thời gian để làm cho nó hoạt động như mong đợi.

Để thư viện của tôi sử dụng các khai báo, tôi đã phải sử dụng phần d.tsmở rộng cho tệp khai báo các thuộc tính mới của đối tượng toàn cục.

Vì vậy, cuối cùng, tập tin đã kết thúc với một cái gì đó như:

/path-to-angular-workspace/angular-workspace/projects/angular-library/src/globals.d.ts

Sau khi tạo, đừng quên để lộ nó trong của bạn public_api.ts.

Điều đó đã làm nó cho tôi. Hi vọng điêu nay co ich.

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.