คุณจะตั้งค่าคุณสมบัติใหม่บน `window` ใน TypeScript ได้อย่างไร?


621

การติดตั้งผม namespaces windowทั่วโลกสำหรับวัตถุของฉันอย่างชัดเจนโดยการตั้งค่าสถานที่ให้บริการใน

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

TypeScript ขีดเส้นใต้MyNamespaceและบ่นว่า:

คุณสมบัติ 'MyNamespace' ไม่มีอยู่ในค่าประเภท 'window' any "

ฉันสามารถทำให้โค้ดทำงานได้โดยการประกาศMyNamespaceว่าเป็นตัวแปรแอมเบียนต์และวางwindowexplicitness แต่ฉันไม่ต้องการทำเช่นนั้น

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

ฉันจะเก็บไว้ที่windowนั่นและทำให้ TypeScript มีความสุขได้อย่างไร

ในฐานะที่เป็นบันทึกด้านข้างฉันพบว่ามันตลกเป็นพิเศษที่ TypeScript บ่นเพราะมันบอกฉันว่าwindowมันเป็นประเภทanyที่โดยแน่นอนสามารถมีอะไร


ดูLink
MHS

คำตอบ:


692

พบเพียงคำตอบนี้ในคำตอบอื่นคำถาม StackOverflow ของ

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

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

โดยทั่วไปคุณจะต้องขยายwindowส่วนต่อประสานที่มีอยู่เพื่อแจ้งให้ทราบเกี่ยวกับคุณสมบัติใหม่ของคุณ


12
สังเกตเมืองหลวง W ใน Window นั่นทำให้ฉันสะดุด
ajm

2
ฉันไม่สามารถคอมไพล์ด้วย tsc 1.0.1.0 ได้ แต่คำตอบของ Blake Mitchell ก็ใช้ได้ผลสำหรับฉัน
Pat

43
โปรดทราบว่าจะใช้งานได้เฉพาะเมื่อประกาศใน.d.tsไฟล์แยกต่างหาก มันไม่ทำงานเมื่อใช้ใน.tsไฟล์ที่ใช้การนำเข้าและส่งออกเอง ดูคำตอบนี้
cdauth

36
การdeclare global { interface Window { ... } }ทำงานกับ TypeScript 2.5.2 ไม่จำเป็นต้องใช้.d.tsไฟล์ตามที่ระบุไว้ข้างต้น
tanguy_k

2
ที่ typescript แรกบอกฉัน: error TS1234: An ambient module declaration is only allowed at the top level in a file.เมื่อฉันวางไว้ที่ด้านบนของไฟล์มันก็แก้ไขข้อผิดพลาดนั้นดังนั้นคุณอาจต้องทำเช่นนั้น
Zelphir Kaltstahl

397

หากต้องการให้มันคงอยู่เพียงใช้:

(<any>window).MyNamespace

9
ความคิดใด ๆ วิธีการทำเช่นนี้ในไฟล์ tsx?
velop

2
@martin: <any> ทำให้ชัดเจนดังนั้นมันใช้ได้ดีฉันเชื่อว่า อื่น ๆ : หากคุณกำลังใช้ typescript กับการตอบสนองหรือสภาพแวดล้อมอื่น ๆ JSX (ผลในไวยากรณ์ TSX) ที่คุณจะต้องใช้แทนas <>ดู @ เดวิดบอยด์คำตอบด้านล่าง
Don

31
ฉันต้องใช้ (หน้าต่างใด ๆ ) MyNamespace
Arsalan Ahmad

ในทำนองเดียวกันสำหรับthis-return (<any> this)['something in scope']
HankCa

1
ฉันต้องปิดการใช้งาน tslint ในบรรทัดนั้นด้วย/* tslint:disable */
Michael Yagudaev

197

ตามประเภท ^ 3.4.3 โซลูชันนี้ไม่ทำงานอีกต่อไป

หรือ...

คุณสามารถพิมพ์:

window['MyNamespace']

และคุณจะไม่ได้รับข้อผิดพลาดในการคอมไพล์และทำงานเหมือนกับการพิมพ์ window.MyNamespace


15
แต่คุณอาจจะได้รับข้อผิดพลาด tslint ... หากคุณมีหนึ่งในหลักสูตร
smnbbrv

69
สิ่งนี้จะต้องเผชิญกับการพิมพ์ที่แข็งแกร่งความคิดทั้งหมดที่อยู่เบื้องหลัง TypeScript
d512

18
@ user1334007 การใช้ globals ก็ทำได้เช่นกัน อย่างไรก็ตามต้องใช้รหัสดั้งเดิมบางรหัส
nathancahill

3
@Ramesh window['MyNamespace']()(เพียงเพิ่มวงเล็บ)
iBaff

6
"สิ่งนี้บินได้อย่างสมบูรณ์เมื่อเผชิญหน้ากับการพิมพ์ที่แข็งแกร่งซึ่งเป็นแนวคิดทั้งหมดที่อยู่เบื้องหลัง TypeScript" ดี!
โคดี

188

ใช้ TSX หรือไม่ ไม่มีคำตอบอื่นใดที่เหมาะกับฉัน

นี่คือสิ่งที่ฉันทำ:

(window as any).MyNamespace

4
เหมือน(<any> window).MyNamespaceจริง
Dmitry Parzhitsky

44
มันเหมือนกันยกเว้นเมื่อใช้ TSX เนื่องจากการ<any>ตีความตีความเป็น JSX ไม่ใช่การส่งแบบ
Jake Boone

1
ขอบคุณ @David สิ่งนี้ทำงานได้อย่างมีเสน่ห์ด้วย Create React App และรุ่น typescript ใหม่ก่อนหน้านี้ใช้งานได้: (หน้าต่าง <any>). MyNamespace แต่ตอนนี้มันแตกใน typescript เวอร์ชัน 3.5.x ใหม่
Jignesh Raval

หน้าต่างเป็นอย่างไร แล้วทำไมคุณต้องใช้ typescript เพียงใช้ Javascript x)
MarcoLe

71

คำตอบที่ยอมรับคือสิ่งที่ฉันเคยใช้ แต่ด้วย TypeScript 0.9. * มันใช้งานไม่ได้อีกต่อไป คำจำกัดความใหม่ของWindowอินเทอร์เฟซดูเหมือนว่าจะแทนที่คำจำกัดความในตัวทั้งหมดแทนที่จะเติมมัน

ฉันได้ทำสิ่งนี้แทน:

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

declare var window: MyWindow;

UPDATE:ด้วย TypeScript 0.9.5 คำตอบที่ยอมรับจะใช้งานได้อีกครั้ง


2
สิ่งนี้ใช้ได้กับโมดูลที่ใช้โดย TypeScript 2.0.8 ตัวอย่าง: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() จากนั้นคุณสามารถโทร foo () เช่นจากคอนโซล Chrome Dev Tools เช่น mc.foo()
Martin Majewski

นี่เป็นคำตอบที่ดีมากถ้าคุณไม่ต้องการประกาศบางสิ่งในโลก ในอีกด้านหนึ่งคุณต้องโทรหาdeclare var...ทุกไฟล์ที่คุณต้องการ
Puce

บวกหนึ่งสำหรับวิธีการนี้เพราะในกรณีนี้คุณไม่ได้ขัดแย้งกับแพคเกจอื่น ๆ ที่ขยายหน้าต่างทั่วโลกในโมโนเรย์
Dmitrii Sorin

ขอบคุณ! คำตอบที่ดีที่สุด IMO หนึ่งไม่ควรแทนที่Windowความจริงง่ายๆว่ามันไม่ใช่หน้าต่างวานิลลา เพียงแค่หลีกเลี่ยงการตรวจสอบประเภทหรือพยายามหลอก TS ก็ไม่ใช่วิธีที่ทำได้
Rutger Willems

สิ่งนี้ใช้ไม่ได้กับ TS 3.6 โชคไม่ดี
Jacob Soderlund

65

ทั่วโลกเป็น "ความชั่วร้าย" :) ฉันคิดว่าวิธีที่ดีที่สุดในการพกพาก็คือ:

ก่อนอื่นให้คุณส่งออกอินเทอร์เฟซ: (เช่น: ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

ที่สองคุณนำเข้า

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

โยนหน้าต่างย่อยส่วนกลางแบบที่สามด้วย CustomWindow

declare let window: CustomWindow;

ด้วยวิธีนี้คุณจะไม่มีเส้นสีแดงใน IDE ที่แตกต่างกันถ้าคุณใช้กับคุณลักษณะที่มีอยู่ของวัตถุหน้าต่างดังนั้นในตอนท้ายลอง:

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

ทดสอบกับ typescript 2.4.x และใหม่ล่าสุด!


1
คุณอาจต้องการขยายว่าทำไม globals ถึงชั่วร้าย ในกรณีของเราเราใช้ API ที่ไม่ได้มาตรฐานบนวัตถุหน้าต่าง ตอนนี้มันเป็นโพลี มันชั่วร้ายจริงหรือ
Mathijs Segers

1
@MathijsSegers ฉันไม่รู้โดยเฉพาะอย่างยิ่งกรณีของคุณ แต่ .. เกี่ยวกับ globals คือความชั่วร้ายไม่เพียง แต่เกี่ยวกับจาวาสคริปต์ .. คือในทุกภาษา .. เหตุผลบางประการคือ: - คุณไม่สามารถใช้รูปแบบการออกแบบที่ดี - ปัญหาหน่วยความจำและประสิทธิภาพ (คุณ มีทุกที่พยายามแนบบางสิ่งกับ String Object และดูว่าเกิดอะไรขึ้นเมื่อคุณสร้าง String ใหม่ - การชนกันของชื่อทำให้เกิดผลข้างเคียงกับรหัส .. (โดยเฉพาะจาวาสคริปต์ที่มีลักษณะเป็น async) ฉันสามารถทำต่อไปได้ พักผ่อน ...
onalbi

1
@onabi แท้จริงฉันกำลังพูดถึงคุณสมบัติของเบราว์เซอร์ สามารถใช้ได้ทั่วโลกเนื่องจากเป็นเบราว์เซอร์ คุณอยากจะแนะนำว่ามันยังผิดที่จะไปกับคำจำกัดความสากล? ฉันหมายความว่าเราสามารถใช้ Ponyfills ได้ แต่สิ่งนี้จะขยายเบราว์เซอร์ที่รองรับคุณลักษณะ (เช่น fullscreen api) ที่กล่าวว่าฉันไม่เชื่อว่ากลมทั้งหมดเป็นชั่วร้าย
Mathijs Segers

2
@MathijsSegers ในความคิดของฉันตัวแปรทั่วโลกควรหลีกเลี่ยงที่จะใช้หรือแก้ไขที่เราสามารถ .. เป็นความจริงที่ว่าหน้าต่างสามารถใช้ได้ตั้งแต่เบราว์เซอร์ที่มีอยู่ แต่ยังเป็นความจริงที่อยู่ในการกลายพันธุ์ตลอดเวลา .. ดังนั้นถ้าเช่น window.feature = 'คุณสมบัติ'; และสิ่งนี้ถูกใช้อย่างมากกับรหัส .. จะเกิดอะไรขึ้นถ้า window.feature ถูกเพิ่มโดยเบราว์เซอร์ในรหัสทั้งหมดคุณสมบัตินี้จะถูกแทนที่ .. ต่อไปฉันให้คำอธิบายของประโยคของฉันไม่ได้ไปกับคุณ .. ขอแสดงความนับถือ ...
onalbi

48

หากคุณต้องการขยายwindowวัตถุด้วยประเภทที่กำหนดเองที่ต้องใช้importคุณสามารถใช้วิธีการต่อไปนี้:

window.d.ts

import MyInterface from './MyInterface';

declare global {
    interface Window {
        propName: MyInterface
    }
}

ดู 'การเพิ่มทั่วโลก' ในส่วน 'การประกาศการรวม' ของคู่มือ: https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation


43

สำหรับผู้ใช้งานAngular CLIมันตรงไปตรงมา:

src / polyfills.ts

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

ฉัน-กำหนดเอง utils.ts

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

หากคุณกำลังใช้ IntelliJ คุณต้องเปลี่ยนการตั้งค่าต่อไปนี้ใน IDE ก่อนที่จะรับไฟล์ polyfills ใหม่:

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

1
declare globalเคล็ดลับและคำตอบนี้ไม่ได้เฉพาะเจาะจงมากที่จะเชิงมุม CLI ...
Avindra Goolcharan

31

ฉันไม่จำเป็นต้องทำสิ่งนี้บ่อยครั้งสิ่งเดียวที่ฉันมีคือเมื่อใช้ Redux Devtools กับมิดเดิลแวร์

ฉันทำ:

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

หรือคุณสามารถทำ:

let myWindow = window as any;

แล้ว myWindow.myProp = 'my value';


1
ในกรณีนี้คุณยังสามารถติดตั้งแพ็กเกจ npm ซึ่งมีคำจำกัดความทั้งหมด - ตามที่ระบุในเอกสาร
bbrinx

คุณสามารถทำได้ แต่นั่นไม่ใช่คำตอบของคำถาม แต่เป็นการแก้ปัญหา
Vitalij

ด้วยเหตุนี้ฉันจึงสามารถเปิดใช้งานเครื่องมือ redux อย่างถูกต้องภายใต้ typescript โดยใช้ (หน้าต่างใด ๆ ) .__ REDUX_DEVTOOLS_EXTENSION__ && (หน้าต่างเป็นใด ๆ ) .__ REDUX_DEVTOOLS_EXTENSION __ ())
danivicario

20

คำตอบอื่น ๆ ส่วนใหญ่ไม่สมบูรณ์

  • บางคนก็ระงับการอนุมานประเภทสำหรับร้านค้า
  • บางคนสนใจเฉพาะตัวแปรโกลบอลเป็นเนมสเปซ แต่ไม่เป็นอินเทอร์เฟซ / คลาส

ฉันพบปัญหาที่คล้ายกันเมื่อเช้านี้ ฉันพยายาม "แก้ปัญหา" จำนวนมากบน SO แต่ไม่มีข้อผิดพลาดประเภทใดเลยและเปิดใช้งานการข้ามประเภทการเรียกใน IDE (webstorm หรือ vscode)

ในที่สุดจากที่นี่

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

ฉันหาวิธีแก้ไขที่เหมาะสมเพื่อแนบ typings สำหรับตัวแปรทั่วโลกซึ่งทำหน้าที่เป็น interface / class และ namespace ทั้งสองทั้ง

ตัวอย่างด้านล่าง:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

ตอนนี้โค้ดข้างต้นผสานการพิมพ์เนมสเปซMyNamespaceและอินเทอร์เฟซMyNamespaceลงในตัวแปรโกลบอลmyNamespace(คุณสมบัติของหน้าต่าง)


2
ขอบคุณ - นี่เป็นสิ่งเดียวที่คุณสามารถใช้ในบริบทที่ไม่ใช่โมดูลโดยรอบ
Tyler Sebastian

18

หลังจากค้นหาคำตอบแล้วฉันคิดว่าหน้านี้อาจมีประโยชน์ https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation ไม่แน่ใจเกี่ยวกับประวัติของการประกาศการรวม แต่มันอธิบายว่าทำไมการทำงานต่อไปนี้ถึงได้

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

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

13

นี่คือวิธีการทำถ้าคุณใช้TypeScript Definition Manager !

npm install typings --global

สร้างtypings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

ติดตั้งการพิมพ์แบบกำหนดเองของคุณ:

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

เสร็จแล้วใช้มัน! typescript จะไม่บ่นอีกต่อไป:

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

1
FWIW typingsนั้นล้าสมัยด้วย Typescript 2.0 (กลางปี ​​2559) และถูกเก็บถาวรโดยเจ้าของ
mrm

13

Typscript ไม่ดำเนินการตรวจสอบชนิดกับคุณสมบัติสตริง

window["newProperty"] = customObj;

เป็นการดีที่ควรหลีกเลี่ยงสถานการณ์ที่เกิดขึ้นทั่วโลก ฉันใช้มันบางครั้งเพื่อดีบักวัตถุในคอนโซลของเบราว์เซอร์



8

หากคุณใช้ Typescript 3.x คุณอาจไม่สามารถใช้declare globalส่วนที่เหลือในคำตอบอื่น ๆ ได้และใช้แทน:

interface Window {
  someValue: string
  another: boolean
}

สิ่งนี้ใช้ได้กับฉันเมื่อใช้ Typescript 3.3, WebPack และ TSLint


^3.4.3ไม่ทำงานใน คำตอบนี้ใช้ได้สำหรับฉันstackoverflow.com/a/42841166/1114926
Green

7

สำหรับการอ้างอิง (นี่คือคำตอบที่ถูกต้อง):

ภายใน.d.tsไฟล์คำจำกัดความ

type MyGlobalFunctionType = (name: string) => void

หากคุณทำงานในเบราว์เซอร์คุณเพิ่มสมาชิกในบริบทหน้าต่างของเบราว์เซอร์โดยเปิดอินเทอร์เฟซของหน้าต่างใหม่:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

แนวคิดเดียวกันสำหรับ NodeJS:

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

ตอนนี้คุณประกาศตัวแปรรูท (ที่จริงจะอยู่บนหน้าต่างหรือทั่วโลก)

declare const myGlobalFunction: MyGlobalFunctionType;

จากนั้นใน.tsไฟล์ปกติแต่นำเข้าเป็นผลข้างเคียงคุณจะใช้งานจริง:

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

และในที่สุดก็ใช้มันที่อื่นใน codebase ด้วย:

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

myGlobalFunction("Kevin");

ขอบคุณนี่เป็นตัวอย่างที่ดีที่สุดที่ฉันพบและทำงานได้ดี
Nick G.

6

ทำให้ส่วนต่อประสานที่กำหนดเองขยายหน้าต่างและเพิ่มคุณสมบัติที่กำหนดเองของคุณเป็นตัวเลือก

จากนั้นให้ customWindow ที่ใช้อินเทอร์เฟซที่กำหนดเอง แต่ให้คุณค่ากับหน้าต่างเดิม

มันทำงานร่วมกับ typescript@3.1.3

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {} 

5

สำหรับผู้ที่ต้องการตั้งค่าคุณสมบัติการคำนวณหรือแบบไดนามิกบนwindowวัตถุที่คุณจะพบว่าไม่เป็นไปได้ด้วยdeclare globalวิธีการ เพื่อชี้แจงกรณีการใช้งานนี้

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

คุณอาจพยายามทำอะไรแบบนี้

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

ข้างต้นจะเกิดข้อผิดพลาด นี่เป็นเพราะใน typescript อินเทอร์เฟซไม่เล่นได้ดีกับคุณสมบัติที่คำนวณและจะโยนข้อผิดพลาดเช่น

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

ในการหลีกเลี่ยงสิ่งนี้คุณสามารถไปกับคำแนะนำในการคัดเลือกwindowเพื่อ<any>ให้คุณสามารถทำได้

(window as any)[DynamicObject.key]

3
นี่คือปี 2019
Qwerty

4

การใช้ create-react-app v3.3 ฉันพบวิธีที่ง่ายที่สุดในการบรรลุเป้าหมายนี้คือการขยายWindowประเภทในการสร้างอัตโนมัติreact-app-env.d.ts:

interface Window {
    MyNamespace: any;
}

3

ก่อนอื่นคุณต้องประกาศวัตถุหน้าต่างในขอบเขตปัจจุบัน
เพราะ typescript ต้องการทราบชนิดของวัตถุ
เนื่องจากวัตถุหน้าต่างถูกกำหนดไว้ที่อื่นคุณจึงไม่สามารถกำหนดได้
แต่คุณสามารถประกาศได้ดังนี้: -

declare var window: any;

นี้จะไม่ redefine windowวัตถุหน้าต่างหรือมันจะไม่สร้างตัวแปรอื่นที่มีชื่อ
ซึ่งหมายความว่าหน้าต่างถูกกำหนดที่อื่นและคุณเพิ่งอ้างอิงในขอบเขตปัจจุบัน

จากนั้นคุณสามารถอ้างถึงวัตถุ MyNamespace ของคุณได้โดย: -

window.MyNamespace

หรือคุณสามารถตั้งค่าคุณสมบัติใหม่บนwindowวัตถุได้ง่ายๆโดย: -

window.MyNamespace = MyObject

และตอนนี้ตัวพิมพ์ดีดจะไม่บ่น


ฉันขอทราบกรณีการใช้งานของคุณที่ต้องการทราบว่าwindowมีการกำหนดไว้ที่ไหน?
Mav55

2

ฉันต้องการใช้สิ่งนี้ในห้องสมุด Angular (6) วันนี้และฉันใช้เวลาสักพักกว่าจะได้ผลตามที่คาดหวัง

เพื่อให้ห้องสมุดของฉันใช้การประกาศฉันต้องใช้ส่วนd.tsขยายสำหรับไฟล์ที่ประกาศคุณสมบัติใหม่ของวัตถุทั่วโลก

ดังนั้นในที่สุดไฟล์ก็จบลงด้วยสิ่งที่ชอบ:

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

public_api.tsเมื่อสร้างแล้วไม่ลืมที่จะเปิดเผยในของคุณ

นั่นทำเพื่อฉัน หวังว่านี่จะช่วยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.