接上一篇《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>
}
}
然後看到對應效果展示