react-native 解析如何編寫.d.ts聲明文件

接上一篇《react-native 爲本地js和開源庫的js編寫.d.ts聲明文件》,本篇分析爲在react-native中應用js文件,我是如何寫聲明文件 *.d.ts 的。

在學習Typescript時,關於如何編寫聲明文件。文檔中提到,可參考、學習開源庫中的源碼聲明。因此,源碼中必然能給我們代碼更大實踐效果上的收穫!

編寫聲明文件靈感分析從兩個方面:

1,源碼分析,node-modules/@Types/react-native是如何編寫聲明全局變量;
2,源碼分析,node-modules/react-native-ui-lib 是如何編寫聲明文件的;
3,實操分析,對本地自己個寫的js代碼,是如何編寫聲明文件的;

分析源碼node-modules/@Types/react-native

打開源碼目錄 [node-moduels/@Types/react-native](https://github.com/wix/react-native-ui-lib),結合react-native的編碼習慣。可以發現 index.d.ts 必定是最終面向開發者使用的聲明文件了。
在這裏插入圖片描述

// index.d.ts
  	...
... 代碼省略...
  	...
//
// Prop Types
//
export const ColorPropType: React.Validator<string>;
export const EdgeInsetsPropType: React.Validator<Insets>;
export const PointPropType: React.Validator<PointPropType>;
export const ViewPropTypes: React.ValidationMap<ViewProps>;

declare global {
    interface NodeRequire {
        (id: string): any;
    }

    var require: NodeRequire;

    /**
     * Console polyfill
     * @see https://facebook.github.io/react-native/docs/javascript-environment.html#polyfills
     */
    interface Console {
        error(message?: any, ...optionalParams: any[]): void;
        info(message?: any, ...optionalParams: any[]): void;
        log(message?: any, ...optionalParams: any[]): void;
        warn(message?: any, ...optionalParams: any[]): void;
        trace(message?: any, ...optionalParams: any[]): void;
        debug(message?: any, ...optionalParams: any[]): void;
        table(...data: any[]): void;
        groupCollapsed(label?: string): void;
        groupEnd(): void;
        group(label?: string): void;
        disableYellowBox: boolean;
        ignoredYellowBox: string[];
    }

    var console: Console;

    /**
     * This contains the non-native `XMLHttpRequest` object, which you can use if you want to route network requests
     * through DevTools (to trace them):
     *
     *   global.XMLHttpRequest = global.originalXMLHttpRequest;
     *
     * @see https://github.com/facebook/react-native/issues/934
     */
    const originalXMLHttpRequest: any;

    const __BUNDLE_START_TIME__: number;
    const ErrorUtils: ErrorUtils;

    /**
     * This variable is set to true when react-native is running in Dev mode
     * Typical usage:
     * <code> if (__DEV__) console.log('Running in dev mode')</code>
     */
    const __DEV__: boolean;

    const HermesInternal: null | {};
}

然後則會發現聲明全局對象的 declare global {}。結合我們平時使用過程中有用到打印日誌的方法console.log('這裏打印日誌 - 聲明:全局對象-全局變量')。可以確定console是已被聲明的全局變量。那麼我們個人應該怎麼通過·global·擴展自己個的全局變量。
當然擴展全局變量,在Typescript這門技術語言中是有自己的一套系統定義的。中文文檔有說道,在 npm 包或 UMD 庫中擴展全局變量的方式。 而通過實踐對比來看,在react-native中要對全局進行擴展變量和npm 包或 UMD 庫中擴展全局變量的方式是一致的。如在這裏對全局變量進行擴展,通過 —— declare global

// */namesp/index.d.ts
//
// Prop Types
// 
export {};
declare global {

    var gHeight: string | number;
    var gWidth: string | number;
}

如果 註釋掉這行代碼 export {}; 會有什麼變化?export {}; 是做什麼用的?
在這裏插入圖片描述

會發現有錯誤提示:Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.ts(2669)[全局作用域的擴展只能直接嵌套在外部模塊或環境模塊聲明中。ts(2669)]
外部模塊的聲明方式則是 export 方式。內部模塊的聲明方式是namespace方式。所以是提示我們在當前的index.d.ts聲明文件中進行全局作用域的擴展,要以 export 方式進行聲明使得index.d.ts聲明文件的作用域成爲外部模塊。[用來告訴編譯器這是一個模塊的聲明文件,而不是一個全局變量的聲明文件。]

分析源碼node-modules/react-native-ui-lib

聲明文件的內容依照什麼編寫?

在這裏插入圖片描述
截圖展示開源庫react-native-ui-lib的目錄結構。
其中圖中看到 index.d.ts 是爲該開源庫所做的聲明文件的總的對外輸出。沿着這條線索追蹤 ~ 選擇export * from './components'; 進入可看到聲明文件 *\components\index.d.ts

// */node_modules/react-native-ui-lib/typings/components/index.d.ts

export * from './ActionBar';
export * from './ActionSheet';
export * from './Avatar';
export * from './Badge';
export * from './Button';
export * from './Card';
... ...
export * from './Text';
export * from './Toast';
export * from './TouchableOpacity';
export * from './Tour';
export * from './View';
export * from './WheelPickerDialog';

在此選擇較熟悉的組件 export * from './Toast'; 進入看源碼

// */node_modules/react-native-ui-lib/typings/components/Toast.d.ts

import {ReactElement} from 'react';
import {
  GestureResponderEvent,
  ImageRequireSource,
  StyleProp,
  TextStyle
} from 'react-native';
import {BaseComponent} from '../commons';
import {ColorValue} from '../style/colors';
import {ButtonProps} from './Button';
import {BlurViewProperties} from '@react-native-community/blur';

export type ToastPosition = "relative" | "top" | "bottom";

export interface ToastProps {
  visible?: boolean;
  position?: ToastPosition;
  height?: number;
  backgroundColor?: ColorValue;
  color?: ColorValue;
  message?: string;
  messageStyle?: StyleProp<TextStyle>;
  icon?: ImageRequireSource;
  actions?: ReadonlyArray<ButtonProps>;
  onDismiss?: (event: GestureResponderEvent) => void;
  autoDismiss?: number;
  allowDismiss?: boolean;
  onAnimationEnd?: (visible: boolean) => void;
  renderContent?: (props: ToastProps) => ReactElement | ReactElement[] | null;
  centerMessage?: boolean;
  animated?: boolean;
  enableBlur?: boolean;
  blurOptions?: BlurViewProperties;
  zIndex?: number;
}

export class Toast extends BaseComponent<ToastProps> {}

看 Toast.d.ts 該土司組件的聲明文件,第38行,通過export class Toast extends BaseComponent<ToastProps> {} 在聲明文件中使用 export 導出,然後在使用方 import 導入,應用這些類型聲明。且在類型聲明上,繼承的父類BaseComponent具有泛型約束<ToastProps> , ts語言使用關鍵字 interface 進行接口類型定義,規範組件Toast的props屬性即作類型檢查。等同於js語言組件的PropTypes進行類型檢查

// */Greeting .js

 import PropTypes from 'prop-types';
 import {Text} from 'react-native';
 class Greeting extends React.Component {
  static propTypes = {
   name: PropTypes.string,
   requiredFunc: PropTypes.func.isRequired,
   requiredAny: PropTypes.any.isRequired,
   // 枚舉類型。
   optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  };
  render() {
    return (
      <Text>Hello, {this.props.name}</Text>
    );
  }
}

聲明文件作用就是爲ts語言環境引用js語言代碼 。 既然是有關Toast.d.ts聲明文件,那麼必然會有對應的Toast.js文件存在。查!
在這裏插入圖片描述
因此 Ts目錄*/node-modules/react-native-ui-lib/typings/components/Toast.d.ts 對應
Js目錄*/node-modules/react-native-ui-lib/src/components/toast/index.js

// */src/components/toast/index.js
import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import {AccessibilityInfo, findNodeHandle, StyleSheet, Animated, Easing, ActivityIndicator} from 'react-native';
import {
  ThemeManager,
  Assets,
  Colors,
  Typography,
  BorderRadiuses,
  PureBaseComponent,
  View,
  Image,
  Button,
  Text
} from 'react-native-ui-lib';

// Create animated view base on uilib view for the safeArea support
const AnimatedView = Animated.createAnimatedComponent(View);
const COLOR = Colors.white;

/**
 * @description: A toast component
 * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/ToastsScreen.js
 */
export default class Toast extends PureBaseComponent {
  static displayName = 'Toast';
... ... 
  render() {
     ... ...
    );
  }
}

... ...
}

觀察Ts的聲明文件 Toast.d.ts 發出疑問 —— interface ToastProps{} 爲什麼在這裏寫?它其中的元素屬性又是怎麼來的?
回答這個問題前,首先我們要明白。xx.d.ts 文件是用來聲明 index.js 文件的。暴露 index.js 文件中的組件、屬性、方法等給tsx文件以供使用。
所以,xx.d.ts 中編寫的聲明內容完全是以index.js作爲依照來編寫的。皮之不存,毛將焉附,沒有Toast的index.js,就無從編寫Toast.d.ts聲明文件。通過Ts聲明文件Toast.d.ts 與 Js的Toast組件 index.js文件對比,便可以證明這一點! 比如,Toast.d.ts 中的屬性約束接口ToastProps

//   */Toast.d.ts 
... ... 
export interface ToastProps {
  visible?: boolean;
  position?: ToastPosition;
  height?: number;
  backgroundColor?: ColorValue;
  color?: ColorValue;
  message?: string;
  messageStyle?: StyleProp<TextStyle>;
  icon?: ImageRequireSource;
  actions?: ReadonlyArray<ButtonProps>;
  onDismiss?: (event: GestureResponderEvent) => void;
  autoDismiss?: number;
  allowDismiss?: boolean;
  onAnimationEnd?: (visible: boolean) => void;
  renderContent?: (props: ToastProps) => ReactElement | ReactElement[] | null;
  centerMessage?: boolean;
  animated?: boolean;
  enableBlur?: boolean;
  blurOptions?: BlurViewProperties;
  zIndex?: number;
}
...   ...

對應於Toast的Js文件內容的類型檢查屬性propTypes 如下

//   */Toast/index.js
... ...
static propTypes = {
    /**
     * Whether to show or hide the toast
     */
    visible: PropTypes.bool,
    /**
     * The position of the toast. 'top' or 'bottom'.
     */
    position: PropTypes.oneOf(['top', 'bottom']),
    /**
     * custom zIndex for toast
     */
    zIndex: PropTypes.number,
    /**
     * The background color of the toast
     */
    backgroundColor: PropTypes.string,
    /**
     * the toast content color (message, actions labels)
     */
    color: PropTypes.string,
    /**
     * the toast message
     */
    message: PropTypes.string,
    /**
     * should message be centered in the toast
     */
    centerMessage: PropTypes.bool,
    /**
     * a left icon
     */
    icon: PropTypes.number,
    /**
     * a single action for the user
     */
    action: PropTypes.shape(Button.propTypes),
    /**
     * should show a loader (showDismiss must be false)
     */
    showLoader: PropTypes.bool,
    /**
     * callback for dismiss action
     */
    onDismiss: PropTypes.func,
    /**
     * number of milliseconds to automatically invoke the onDismiss callback
     */
    autoDismiss: PropTypes.number,
    /**
     * show dismiss action (right 'X' button)
     */
    showDismiss: PropTypes.bool,
    /**
     * callback for end of component animation
     */
    onAnimationEnd: PropTypes.func,
    /**
     * render a custom view that will appear permanently above or below a Toast,
     * depends on the Toast's position, and animate with it when the Toast is made visible or dismissed
     */
    renderAttachment: PropTypes.elementType,
    /**
     * render a custom loader component instead of the default when passing showLoader
     */
    customLoader: PropTypes.func
  };
  ... ...

聲明文件的內容怎麼暴露對外?

export class Toast extends BaseComponent<ToastProps> {}
分析:以npm 包的聲明文件方式進行聲明 —— export class *** *** 。
聲明文件中不能有實現,因此我們會經常看到這種格式:

export type XXX = 'default' | 'hue' ;
export interface XXX{
  value?: number;
 ...
}
export class XXX extends XX<T> {}

而且會發現該開源庫中的組件,全以這格式作文件聲明!在研究聲明文件Toast.d.ts時發現有這樣的一些代碼

// */node_modules/react-native-ui-lib/typings/components/Toast.d.ts

import {ReactElement} from 'react';
import {
  GestureResponderEvent,
  ImageRequireSource,
  StyleProp,
  TextStyle
} from 'react-native';
import {BaseComponent} from '../commons';
import {ColorValue} from '../style/colors';
import {ButtonProps} from './Button';
... ...

第12行代碼,import {ButtonProps} from './Button'; 進入查看源碼,和Toast.d.ts一樣正常定義接口約束和導出模塊export interface ButtonProps extends TextProps {...},並且./Button 文件可以在定義聲明文件的目錄./node-modules/typings 中找到。 但是import {ColorValue} from '../style/colors';就大不相同,因爲 …/style/colors 文件不在聲明文件的目錄 ./node-modules/typings 中,而是在Js源碼文件 ./node-modules/src/中。 說明這種類型的js文件是可以在ts文件中被使用。而不必然不能在tsx文件中使用。 這種引用方式很特別且很實用和方便。且看colors.js源碼

// node_modules\react-native-ui-lib\src\style\colors.js

import _ from 'lodash';
//@ts-ignore
import Color from 'color';
import tinycolor from 'tinycolor2';
import { colorsPalette } from './colorsPalette';
//@ts-ignore
import ColorName from './colorName';
export class Colors {
    ... ...
    /**
     * Load custom set of colors
     * arguments:
     * colors - map of keys and colors values e.g {dark10: '#20303C', dark20: '#43515C'}
     */
    loadColors(colors) {
        _.forEach(colors, (value, key) => {
            this[key] = value;
        });
    }
    ... ...
const TypedColors = Colors;
const colorObject = new TypedColors();
colorObject.loadColors(colorsPalette);
export default colorObject;

動態的將 import { colorsPalette } from './colorsPalette';

// node_modules\react-native-ui-lib\src\style\colorsPalette.js
const colorsPalette = {
    // DARKS TODO: deprecate and use greys
    dark10: '#20303C',
    dark20: '#43515C',
    dark30: '#66737C',
   ... ...
    // WHITE,
    white: '#FFFFFF',
    black: '#000000'
};
// For Eslint --fix
const extraFixColorsMap = {
    black: 'black',
    white: 'white',
    '#000': 'black',
    '#fff': 'white'
};
export { colorsPalette, extraFixColorsMap };

以方式import {ColorValue} from '../style/colors';添加成爲Toast.d.ts 中的聯合類型 。
截至此時,該開源庫有bug尚未解決,bug問題不是本blog研究的方向。這裏簡要提一嘴。如toast的index.js中類型檢查的屬性renderAttachment與Toast.d.ts聲明文件中renderContent的命名不一致、不一樣。在使用時會發現不起作用,因此需要我們手動修改回來。

開源組件使用

在組件使用上,使用 import {Toast, Button} from 'react-native-ui-lib';由於在配置屬性上會有報錯提示,警告!但是不影響使用。警告提示import {Toast, Button} from 'react-native-ui-lib';
在這裏插入圖片描述
效果展示:
在這裏插入圖片描述

實操分析,爲本地js編寫聲明文件

問題回顧:聲明文件index.d.ts 這類文件叫做聲明文件,爲提供ts語言兼容js語言而存在 —— 即在ts中使用js的代碼。結合項目實際開發中,我們會用到自定義的全局變量js第三方開源庫本地js代碼 。以上已經分析過了前2類使用方式,第三類本地js代碼若要提供給ts使用,怎麼編寫聲明文件?

大致有幾類這裏總結下

  • declare var 聲明全局變量
  • declare function 聲明全局方法
  • declare class 聲明全局類
  • declare enum 聲明全局枚舉類型
  • declare namespace 聲明(含有子屬性的)全局對象
  • interface 和 type 聲明全局類型

這幾類讀起來我們肯定能很容易就理解的。但是如何結合js文件纔是關鍵!
那麼我就開始舉例說明了 ~
下面描述的聲明文件方式,和上面總結的幾類是對應着進行舉例描述的。
編寫本地將要使用的js文件 jsmodule.js

// */jsmodule.js

/**
 * 手寫聲明文件,使用js程序 
 */


/**
 * 手寫聲明文件,使用js程序 
 */

export const DEVICE_VERSION = 'Android 版本 - 10';

export function express(params){
  console.log('function - express 方法');
  if(typeof params == 'number'){
    return 'function - express number方法 ->' + params;

  } else if(typeof params == 'string'){

    return 'function - express string方法 ->' + params;
  } else {
    
    return 'function - express no-param方法' ;
  }
}

export class StringChecker {
   deviceHeight = '1920px';
   devoceWidth = '1080px';

   checkerEmpety = ()=>{
     return 'hello empety'+this.deviceHeight + '-' +this.devoceWidth;
   }
}

編寫爲ts使用js的聲明文件jsmodule.d.ts

// */jsmodule.d.ts

declare const DEVICE_VERSION: string;
declare const express: (params?:string | number)=>string;
declare class StringChecker{
  deviceHeight: string | number;
  checkerEmpety(): string;
}
export {StringChecker, DEVICE_VERSION, express};

react-native中的.tsx文件中具體使用
在 npm 包的聲明文件中,使用 declare 不再會聲明一個全局變量,而只會在當前文件中聲明一個局部變量。

// */screens/OnlineContact.tsx

import React from 'react';
import {
  View,
  Text,
  Image,
} from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import DeviceConfs from '../confs/DeviceConfs';
import ImgConfs from '../../src/confs/ImgConfs';
import {StringChecker, DEVICE_VERSION, express} from '../libs/js-module/jsmodule';
import LettersOnlyValidator from '../libs/namesp/LettersOnlyValidator';

interface OnlineContactProps {
  id: string;
  schecker: StringChecker;
}
interface OnlineContactState {
  tabIndex: number;
}
export default class OnlineContact extends React.Component<OnlineContactProps, OnlineContactState>{

  schecker = new StringChecker();
  constructor(props: OnlineContactProps){
    super(props);
    this.state = {
      tabIndex: 0
    }
  }
  
  render(){
    
    return <View style={{flex:1, alignItems:'center', backgroundColor: 'white'}}>
      {/* 標題欄 */}
        <View style={{width: DeviceConfs.sWidth, height: 42, 
            flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between'}}>
            <TouchableOpacity onPress={()=>{this.props.navigation.pop()}} activeOpacity = {0.8}
              style={{flexDirection:'row', alignItems: 'center', width:DeviceConfs.sWidth * 0.3}}>
              <Image source={ImgConfs.ic_goback} resizeMode={'contain'} 
              style={{width: 38, height: 38}}/>
              <Text style={{color: '#444', fontWeight: 'bold', fontSize: 14, alignSelf: 'center', marginLeft: -6}}>{'返回'}</Text>
            </TouchableOpacity>
          <View style={{width:DeviceConfs.sWidth * 0.3, alignItems: 'center', justifyContent:'center'}}>
            <Text style={{color: '#333', fontWeight: 'bold', fontSize: 18}}>{'TS功能測試'}</Text>
          </View>
          <View style={{width:DeviceConfs.sWidth * 0.3}}/>
        </View>
        <View style={{height:4, width: DeviceConfs.sWidth, backgroundColor: '#f4f4f4'}}/>

        {/* 寫入測試的代碼 */}
        
        {/* 自定義聲明文件使用本地js */}
        <View style= {{width:DeviceConfs.sWidth, height: DeviceConfs.sHeight ,
            padding: 30 ,alignItems: 'center'}}>
              <Text style={{fontSize: 16, color: '#e48723', alignSelf: 'flex-start'}}>
              {'自寫聲明文件&本地js - DEVICE_VERSION:'}{'\n'}{DEVICE_VERSION}
              </Text>
              <Text style={{fontSize: 16, color: '#e46c23', alignSelf: 'flex-start', marginVertical:15}}>
              {'自寫聲明文件&本地js - express:'}{'\n'}{express('中國大愛')}
              </Text>
              <Text style={{fontSize: 16, color: '#e44723', alignSelf: 'flex-start'}}>
              {'自寫聲明文件&本地js - StringChecker:'}{'\n'}{new StringChecker().checkerEmpety()}
              </Text>
        </View>

      </View>
  }
}

展示下使用之後的效果展示:
在這裏插入圖片描述
需要注意的一點是 在環境上下文中不允許初始化器。 即在聲明文件中是不允許做變量賦值和方法、類的具體實現的。
在這裏插入圖片描述
需要注意的二點是 *.js 文件中,類似下面片段代碼

// */StringChecker.js
export class StringChecker {
   deviceHeight = '1920px';
   ... ...

如果要在 class StringChecker 中的方法中引用,則需要用到上下文環境,如下'hello empety'+this.deviceHeight + '-' +this.devoceWidth;。否則會報錯:找不到變量deviceHeight 和 devoceWidth。

// */StringChecker.js
export class StringChecker {
   deviceHeight = '1920px';
   devoceWidth = '1080px';

   checkerEmpety = ()=>{
     return 'hello empety'+this.deviceHeight + '-' +this.devoceWidth;
   }
}

上面描述了 變量(let、const和var)、方法(function)、類(class)的聲明,還缺少了對象(namespace)、類型(interface 和 type) 和 枚舉(枚舉)的具體分析。
暫且不論 declare enum , 且分析declare namespace和interface 和 type的使用。

定義 *js 文件,以es6的語法糖方式進行編寫

// */namesp/ZipCodeValidator.js

// const numberRegexp = /^[0-9]+$/;
export default class ZipCodeValidator {
    isAcceptable(s) {

        return "ZipCodeValidator - 使用export namespace 聲明文件";
    }

    printZLCodeValidator(param){

        return {method:'國人', key:'都在議', value: param}
    }
}

之後,另定義一個 *.js 文件,引用js類ZipCodeValidator ,以es6的語法糖方式進行編寫

// */namesp/LettersOnlyValidator.js

import ZipCodeValidator from "./ZipCodeValidator";

// const lettersRegexp = /^[A-Za-z]+$/;
class LettersOnlyValidator {
    isAcceptable(s) {
        return "LettersOnlyValidator - 使用export namespace 聲明文件";
    }
}
LettersOnlyValidator.ZipCodeValidator = ZipCodeValidator;
export default LettersOnlyValidator;

這裏邊讓我很驚豔的是這一行代碼,因爲之前從未使用過,且這種使用方式是從·react-native-ui-lib源碼·中發現的
LettersOnlyValidator.ZipCodeValidator = ZipCodeValidator;
通過以上兩個js文件的class類的定義及相引用,則在數據結構上會產生一個新的有層級的數據結構 ,因此也就很自然的對應上了命名空間namespace : 命名空間是爲了提供邏輯分組和避免命名衝突

因此編寫聲明文件以提供ts文件使用,其中使用到了字符串索引類型,並以此來詮釋了declare namespace 聲明(含有子屬性的)全局對象 interface 和 type 聲明全局類型的使用。

// */namesp/LettersOnlyValidator.d.ts

declare class LettersOnlyValidator{
    isAcceptable(s?: string): string;
}
 
declare namespace LettersOnlyValidator {
    interface ZLCodeValidator {
        method:'國人'| number;
        key: '都在議' | number;
        value: string;
    }
    // interface 定義的接口約束 等同於下面 type定義的類型約束
    // type ZLCodeValidator = {
    //     method:'國人'| number;
    //     key: '都在議' | number;
    //     value: string;
    // };
    export class ZipCodeValidator{
        isAcceptable(s?: string): string|boolean;

        printZLCodeValidator(param: string): {[key: string]: ZLCodeValidator};
    }
}
export default LettersOnlyValidator;

並且在文件 *.d.ts 中,有看到

...
declare class LettersOnlyValidator
...
...
declare namespace LettersOnlyValidator
...

兩者會進行聲明合併,若以Java語言的語法進行解釋的話,等同於Java中 ZipCodeValidator 是 LettersOnlyValidator的內部類。

此時應用到 *.tsx 中

// */screens/OnlineContact.tsx

import React from 'react';
import {
  View,
  Text,
  Image,
} from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import ImgConfs from '../../src/confs/ImgConfs';
import {StringChecker, DEVICE_VERSION, express} from '../libs/js-module/jsmodule';
import LettersOnlyValidator from '../libs/namesp/LettersOnlyValidator';

interface OnlineContactProps {
  id: string;
  schecker: StringChecker;
}
interface OnlineContactState {
  tabIndex: number;
}
export default class OnlineContact extends React.Component<OnlineContactProps, OnlineContactState>{

  schecker = new StringChecker();
  constructor(props: OnlineContactProps){
    super(props);
    this.state = {
      tabIndex: 0
    }
  }
  
  render(){
    
    return <View style={{flex:1, alignItems:'center', backgroundColor: 'white'}}>
      {/* 標題欄 */}
        <View style={{width: DeviceConfs.sWidth, height: 42, 
            flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between'}}>
            <TouchableOpacity onPress={()=>{this.props.navigation.pop()}} activeOpacity = {0.8}
              style={{flexDirection:'row', alignItems: 'center', width:DeviceConfs.sWidth * 0.3}}>
              <Image source={ImgConfs.ic_goback} resizeMode={'contain'} 
              style={{width: 38, height: 38}}/>
              <Text style={{color: '#444', fontWeight: 'bold', fontSize: 14, alignSelf: 'center', marginLeft: -6}}>{'返回'}</Text>
            </TouchableOpacity>
          <View style={{width:DeviceConfs.sWidth * 0.3, alignItems: 'center', justifyContent:'center'}}>
            <Text style={{color: '#333', fontWeight: 'bold', fontSize: 18}}>{'TS功能測試'}</Text>
          </View>
          <View style={{width:DeviceConfs.sWidth * 0.3}}/>
        </View>
        <View style={{height:4, width: DeviceConfs.sWidth, backgroundColor: '#f4f4f4'}}/>

        {/* 寫入測試的代碼 */}
        
        {/* 自定義聲明文件使用本地js */}
        <View style= {{width:gWidth, height: gHeight ,
            padding: 30 ,alignItems: 'center'}}>
              <Text style={{fontSize: 16, color: '#e44723', alignSelf: 'flex-start'}}>
              {'自寫聲明文件&本地js - LettersOnlyValidator:'}{'\n'}
              {new LettersOnlyValidator.ZipCodeValidator().printZLCodeValidator('大美中國')['method']}
              {new LettersOnlyValidator.ZipCodeValidator().printZLCodeValidator('大美中國')['key']}
              {new LettersOnlyValidator.ZipCodeValidator().printZLCodeValidator('大美中國')['value']}
              </Text>
        </View>

      </View>
  }
}

然後看到對應效果展示
在這裏插入圖片描述

文章參考:
https://zh-hans.reactjs.org/docs/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章